Посоветуйте алгоритм решения

Автор siti, 16 мая 2022, 14:10

0 Пользователи и 1 гость просматривают эту тему.

eeigor

Да. Вы и хотели присвоить куче переменных номера колонок.

    if TagExists(Tags,"m0") then
      msgbox "m0 = " & Tags("m0")
    end if
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

siti

присваивает всё хорошо. Проверку не проходит функцией TagExists. False выдает

eeigor

#32
Я пишу с телефона без проверки, поэтому пример функции снабжён пометкой "Demo code".
На вид, вроде, всё верно, но вам "на месте" виднее...
Первым параметром функци следует коллекция oTags.

Edit:
Я не знаю, что у вас там:
   Tag=FillTags(oDoc,0,6)
И почему Tag (в ед. числе)?

Закомментируйте: On Local Error Resume Next
и проанализируйте ошибку.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

siti

#33
Цитата: eeigor от 18 мая 2022, 11:03Закомментируйте: On Local Error Resume Next
и проанализируйте ошибку.

Действие не поддерживается.
Неправильный вызов процедуры

Глобально определены
Dim Tag, TagAP As New Collection


function FillTags(byval Doc, Sheet, Row)
Dim rgs, rg, cell, sh
Dim j&
Dim arr()
Dim TagF As New Collection

sh = Doc.Sheets(Sheet)
rg = sh.getCellRangeByPosition(0, row, 50, row)
arr() = rg.DataArray
rgs = Doc.createInstance("com.sun.star.sheet.SheetCellRanges")
rgs.addRangeAddress(rg.RangeAddress, False)
For Each cell In rgs.Cells
TagF.Add cell.CellAddress.Column, cell.String
Next
FillTags=TagF
end function


В случае ошибки Err=0.
Нет кода ошибки. Из-за этого ничего и не работает.
Error тоже пусто

eeigor

А если наоборот
TagExists = (Err <> 0)
TagExists = (Err = 0)
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

siti

Цитата: eeigor от 18 мая 2022, 12:57
А если наоборот
TagExists = (Err <> 0)
TagExists = (Err = 0)

Err вообще не меняется с ошибкой или без. Всегда 0. Соответственно и функция стабильно возвращает:
false
true
для вариантов выше.

Да и error при ошибке тоже пусто

eeigor

#36
@siti, добрался до компа.
Ошибка была вот здесь:
Function TagExists(oTags As Collection, sTag$) As Boolean  'хотя это не должно влиять, проблема в операторе Resume Next
И не только (в Excel VBA бы сработало, но здесь нет, и надо тестировать).
Смотрите.

Sub Main
Dim oTags As New Collection
Dim sh, rgs, rg, cell
Dim j&, item, s$
Dim arr()

'arr() = Array("gid", "adress", "spl" _
' , "m0", "m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9", "m10", "m11" _
' , "price1", "price2", "price3", "price4", "price5", "photo1", "photo2", "photo3")

sh = ThisComponent.Sheets(0)
rg = sh.getCellRangeByPosition(0, 3, 22, 3)
arr() = rg.DataArray

'For j = 0 To 22
' cell = sh.getCellByPosition(j, 3)
' oColumnHeaders.Add cell.CellAddress.Column, cell.String
'Next

rgs = ThisComponent.createInstance("com.sun.star.sheet.SheetCellRanges")
rgs.addRangeAddress(rg.RangeAddress, False)
For Each cell In rgs.Cells
oTags.Add cell.CellAddress.Column, cell.String
Next

GoTo Jump

s = "Tag Count:  " & oTags.Count & Chr(10)
For Each item In oTags
s = s & " " & item
Next
MsgBox s, , "Column Indices"

MsgBox arr(0)(0) & " = " & oTags(arr(0)(0)), , "Example"

s = "Tag Count:  " & oTags.Count & Chr(10)
For Each item In arr(0)
s = s & Chr(10) & item & " = " & oTags(item)
Next
MsgBox s, , "Extract column index from Collection by Tag"

Jump:
For Each item In Array("gid", "Ку")

If TagExists(oTags, item) Then
MsgBox "Tag """ & item & """ exists.", , "Does Tag Exists?"
Else
MsgBox "Tag """ & item & """ not found.", , "Does Tag Exists?"
End If
Next
End Sub

Function TagExists(oTags As Collection, sTag$) As Boolean
On Local Error GoTo Failed
Dim result
result = oTags(sTag)  'if sTag not found -> Failed
TagExists = True
Failed:
End Function


Updated
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

siti

Хорошо, протестирую.
А код ошибки не должен возвращаться даже в случае этой ошибки?

eeigor

#38
Цитата: siti от 18 мая 2022, 20:35Хорошо, протестирую.
Я исправил код. Скопируйте заново.
Цитата: siti от 18 мая 2022, 20:35А код ошибки не должен возвращаться даже в случае этой ошибки?
Нам код ошибки не сильно нужен (и так всё ясно).

Я не знаю, что там не так с Err... выкинул его совсем вместе с Resume Next. Но будет время, поинтересуюсь...

Edit:
Да, вы правы: Resume Next сбрасывает код ошибки (5). Но это совершенно отличное поведение от VBA. Поэтому надо или обрабатывать ошибку стандартным образом (On Error GoTo Label), или как сделал я: выбросил всё в ответе #36.

Пример неработающего кода
Function TagExists(oTags As Collection, sTag$) As Boolean
   Dim result
   On Local Error Resume Next
   result = oTags(sTag)
   TagExists = (Err = 0)
End Function


Вывод: оператор Resume Next позволяет проигнорировать ошибку, но не позволяет извлечь её номер - вечный ноль. Похоже на баг. Точнее, оператор работает по-другому, чем в VBA.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

siti

Да, всё четко! Спасибо.

Вот проблема которую не могу осилить уже несколько дней. Напишу тут, чтобы не плодить тем.
См. файл пример.
Копирование диапазона с форматированием. Взял за основу готовый макрос с этого форума.
При запуске PresCopyForm - локальное копирование, всё работает. Создает новый лист и туда вставляет шаблон.
А при запуске Main - копирование в новый файл, в этом примере не работает, а в рабочем проекте просто LO вылетает фатально.

eeigor

Цитата: siti от 18 мая 2022, 21:59Напишу тут, чтобы не плодить тем.
Правильно будет создать новую тему и подробнее описать проблему.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

economist

Функции, подобные динамическим массивам Excel - давно реализованы в Pandas (и оттуда же и слямзены):
df.filter('fox', regex=True).T
Нужно всего лишь написать макрос для возврата датафрейма Pandas в Calc - Range, либо реализовать чтение стилизованного dataframe из файла.

Кмк не стоит пытаться переизобретать или превзойти Pandas, поскольку скорость работы, удобство срезов и структуры list, dict, set - обеспечили Питону первое место среди ЯП несмотря на недостатки самого языка.
Возвращать в Calc пересчитанный и трансформированный как надо массив - вот, имхо, наиболее правильное использование двух технологий.
Руб. за сто, что Питоньяк
Любит водку и коньяк!
Потому что мне, без оных, -
Не понять его никак...

eeigor

#42
@economist, я разбирался с dynamic array FILTER function и пр.
Это другое поведение, другая парадигма. Одна ошибка типа #SPILL! чего стоит! Когда на пути есть препятствие, можно его выделить. Убери препятствие - и формула разольётся (spill) по диапазону. Формула помещена в top-left cell, но занятый диапазон очерчен, и в другие ячейки ничего не ввести (занято!).
То, что предлагаете вы, не будет dynamic. Это просто быстро, но не более.

У ТС данных - кот наплакал. Требуется макрос или функция FILTER, заполняющая только диапазон вправо и влево от себя, и всё.
У меня есть намерение реализовать такую функциональность (фильтрацию по горизонтали и по вертикали). Это не скрытие столбцов/строк, и именно извлечение данных (усечение диапазона).

Тут ещё вот в чём дело. Надо задействовать мощности самого листа.
Вот такая UDF запросто создаст логический массив для второго аргумента Include: A5:W5="янв". Насколько простой синтаксис! А в макросе я должен чёрт-те чё писать, вычислять...
   {=FILTER(A5:W25;A5:W5="янв")}
Для функции требуется знать, куда она помещена, чтобы пойти вправо и влево. Так, вводим формулу массива на листе в ячейку, лист вычисляет эти самые массивы (тут в критериях могут использоваться много других функций, реализующих логику AND и OR, работу с датами и пр.), и... заполняется диапазон, первое значение которого возвращается в ячейку вызова (с формулой). Пойдёт? Или я ошибаюсь?

Всё. Больше не сделать... Но можно проверять, свободен ли диапазон (что-то вроде #SPILL!), можно записать значения RangeAddress как коммент к ячейке с формулой и перед обновлением очистить занятый диапазон и заполнить его заново согласно новым условиям фильтра. Никакой "динамики" и визуализации, и ещё: в занятые ячейки можно занести другие данные, чем испортить диапазон (до его обновления). В общем, полуавтомат.

Сегодня я фильтрую только строки как-то так. А надо с учётом ориентации (ByRows|ByColumns).

Function FilterRange(aData, nSearchColumn%, sMatch$ _
, Optional bWildcards As Boolean, Optional bInclude As Boolean)
''' Filter the elements of the original data array, checking them against the sMatch pattern.
''' Arguments:
''' aData: Range data array.
''' nSearchColumn: Number of column to search for.
''' sMatch: Match pattern.
''' bWildcards (optional, default is False):
''' Specifies whether wildcards are enabled in sMatch pattern.
''' bInclude (optional, default is True):
''' If True, the filtered rows will be included.
''' Returns: A zero-based, 1D array of rows that include or exclude a match.

Dim i&, j&, nRowCount&  'row & column indices, filtered row count
Dim bResult As Boolean  'indicates whether or not the string satisfies the pattern

If Not IsArray(aData) Then Exit Function
If IsMissing(bWildcards) Then bWildcards = False
If IsMissing(bInclude) Then bInclude = True

' Move rows that meet criterion to beginning of data array.
nRowCount = 1  'skips header row (column titles)
For i = LBound(aData) + 1 To UBound(aData)
If bWildcards Then
bResult = aData(i, nSearchColumn) Like sMatch
Else
bResult = (aData(i, nSearchColumn) = sMatch)
End If
If IIf(bInclude, bResult, Not bResult) Then
nRowCount = nRowCount + 1
For j = LBound(aData, 2) To UBound(aData, 2)
aData(nRowCount, j) = aData(i, j)  'copies column values of i-th row to nRowCount row
Next j
End If
Next i
' Clear rest of array.
For i = nRowCount + 1 To UBound(aData)
For j = LBound(aData, 2) To UBound(aData, 2)
aData(i, j) = ""
Next j
Next i
FilterRange = aData
End Function


Главная идея в том, чтобы произвести все вычисления (максимально возможные) на самом листе: до передачи аргумента, используя операции с массивами и формулы листа.

Edit:
Я не работал с матричными формулами листа и не знаю, можно ли диапазон для фильтрации "скрестить" с диапазоном булевых значений критериев фильтрации, чтобы избежать циклов в макросе. А если задействовать функцию листа SUMPRODUCT*? То есть вычисляем на листе до передачи аргумента в UDF, и в UDF, используя функции листа.

Тогда весь код - это в основном вычисление размера диапазона, обработка ошибок...

* Нет. SUMPRODUCT потребует одинаковой размерности массивов.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

economist

#43
Цитата: eeigor от 19 мая 2022, 15:18То, что предлагаете вы, не будет dynamic. Это просто быстро, но не более.

В чистом виде - да, "Dynamic by Excel" не будет. Но задачи вроде автофильтра столбцов, решейпинга строк в столбцы и усечения, булевая фильтрация строк/столбцов, группировка, кросс- и сводные таблицы - в Pandas решены легче, чем где-либо (потому что был скопирован самый удачный функционал из Matlab/R, проверенный десятилетиями). Все это делается в "одну строку", с ясным синтаксисом.

Насчет SPILL - есть и такое поведение в Pandas. И применение функций к целым таблицам с авто-игнором нечисловых полей (типа "формулы массива" Excel).

Повторять макросами новые, нужные лишь 10% пользователей фишки Excel, при наличии готового Pandas-решения, кмк, уже поздновато, хоть и полезно для развития, в т.ч. всем кто читает ветку. Я скопировал себе вашу функцию в сетевую библиотеку. Но вот вопрос: как себя заставить/приучить использовать код из ветки и FilterRange() повторно? Что будет с производительностью, когда строк будут десятки тысяч? Что с точностью вычислений? Как обрабатывать NULL-значения?  

В этом смысле понятна позиция части разработчиков LO в части неспешной реализации Excel-фишек - жить без них можно. В следующих версиях Excel мелкомягкими анонсирована тесная интеграция с Python и Pandas, на фоне чего опять нам всем придется что-то переосмысливать. Вот почему изучение Pandas (этот же синтаксис используется в Dask, Spark и др. платформах, в т.ч. для BigData) кажется мне более перспективным, чем реализация динамических массивов как в Excel.
Руб. за сто, что Питоньяк
Любит водку и коньяк!
Потому что мне, без оных, -
Не понять его никак...