[Закрыта] Присвоение данных массива диапазону и назад

Автор eeigor, 15 ноября 2020, 21:30

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

eeigor

Обнаружил поведение, отличное от VBA при выполнении операций, указанных в теме.
' Copies an array to and from a range.
Sub CopyArrayToRange2()
Dim oSheet As Object, oRange As Object
Dim vArr(99, 9) As Variant  '2D-array for range A1:J100

REM vArr = Array(Array("A1", "B1"), Array("A2", "B2"))  'array of arrays (jagged)

Dim r&, c&
For r = 0 To UBound(vArr, 1)
For c = 0 To UBound(vArr, 2)
vArr(r, c) = Chr(c + 65) & r + 1
Next c
Next r

' Копируем данные из массива в диапазон листа.
oSheet = ThisComponent.CurrentController.ActiveSheet
oRange = oSheet.getCellRangeByPosition(0, 0, UBound(vArr, 2), UBound(vArr, 1))
oRange.DataArray = vArr  '.SetDataArray(vArr)
' NOTE: Данные присваиваются, несмотря на то что vArr - это 2D-массив

' Изменяем данные. Делаем что-то ещё...
With oRange.getCellRangeByName("A1")
.String = .String & "*"  'changed
End With

' Копируем данные из диапазона обратно в массив.
' NOTE: Как выяснилось, LO, в отличие от Excel, автоматически
' не преобразует объявленную переменную в 2D-массив, соответствующий
' размерности диапазона ячеек, а присваивает ей массив массивов.
ReDim vArr()

' Осуществляем доступ к элементам внутри массива массивов
' с использованием координат элемента (это не 2D-массив, как хотелось бы).
vArr = oRange.DataArray  '.getDataArray()
MsgBox vArr(0)(0)  '>> A1*
End Sub


Комментарий. Метод диапазона setArrayData() требует массив массивов, но принимая 2D-массив, преобразует его к требуемому виду.
Однако метод getArrayData() возвращает обратно именно массив массивов, требующий другого обращения к элементам:
vArr(row)(column) вместо vArr(row, column)
В результате в коде выше с одной переменной работаем по-разному. Это плохо.

Вопрос: Как получить диапазон листа в виде 2D-массива?. Возможно, надо работать с другими свойствами диапазона. Или что я делаю не так?
UPD: А может, это баг? Два "симметричных" метода работают по-разному...

Справочно:
https://vremya-ne-zhdet.ru/vba-excel/diapazon-yacheyek-i-massiv-obmen-znacheniyami/
https://riptutorial.com/vba/example/16563/jagged-arrays--arrays-of-arrays-
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

sokol92

#1
Документация говорит, что в обоих Get/SetDataArray методах задействованы массивы массивов. В VBA таких методов нет.
Теоретически Вы можете использовать свойство Range.Value (c опцией поддержки VBA) для чтения и записи, однако эффективность такого подхода в современных версиях LO ужасна.
Владимир.

eeigor

#2
Владимир, я, конечно, могу "выполнить задачу", но..
1. "Симметричные" методы - не симметричны.
2. Я привык работать с 2D-массивами, а не с массивами массивов, а тут работать надо именно с ними.
3. Операция переноса данных на лист для их обработки с применением функций диапазона листа вовсе не бесполезна (сортировка, например, работает очень быстро, но надо очень быстро переносить данные, не в цикле же).
4. Диапазон - это таблица (матрица), скорее 2D-массив, чем массив массивов:
   vArr = Array(Array("A1", "B1"), Array("A2", "B2"))  'для ясности вместо значений даны адреса ячеек

И всё-таки, как правильно переносить данные из массива в диапазон листа и обратно. Может, я что-то делаю неправильно?
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

sokol92

#3
1. Методы симметричны - и там, и там массив массивов. То, что Вам удалось (?) "подсунуть" двумерный массив - недокументированная особенность. Цикл обработки аналогичен Excel:
  • выбрали данные диапазона ячеек листа Calc в массив массивов типа Variant (в Excel VBA двумерный массив типа Variant)
  • обработали циклами по строкам и столбцам данные
  • загрузили обратно (если необходимо)
Сами по себе методы чтения/записи значений ячеек работают довольно быстро на "однородных" данных.

2. Я тоже привык, но это не аргумент. Для меня еще непривычно, что пустые ячейки соответствуют пустой строке, а не Empty.

3-4. Зависит от точки зрения.
Владимир.

eeigor

#4
Ссылка выше (VBA). Цитата:
"Стоит отметить, что для копирования значений из диапазона ячеек в массив можно использовать только обычную переменную или динамический массив универсального типа (Variant). VBA Excel автоматически преобразовывает их в двумерный массив. Если объявить двумерный массив с указанной заранее размерностью, использовать его не получится, будет сгенерирована ошибка с сообщением: Can't assign to array (Нельзя назначать массив)".

Но в формах VBA с элементами управления я работал также. Например, при присвоении значений списку и комбобоксу, в которых более одного столбца (через 2D-массив).

UPD: Вот как пришлось исправить код, ибо размерность везде равна 1, а вот вложенность разная:
oRange = oSheet.getCellRangeByPosition(0, 0, UBound(vArr(0)), UBound(vArr))

Цитата: sokol92 от 15 ноября 2020, 22:08То, что Вам удалось (?) "подсунуть" двумерный массив - недокументированная особенность.
Теперь надо, чтобы и обратно... :)
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

sokol92

MS Office VBA и LO Basic - разные системы программирования. Синтаксис языков похож (но есть и важные отличия, например, в реализации массивов), объектные модели совершенно иные.

Если пытаться копировать коды "один в один" из VBA в Basic, то даже если макросы будут работать, эффективность этой работы (как в случае с Range.Value) под большим вопросом.
Владимир.

eeigor

#6
Цитата: sokol92 от 15 ноября 2020, 22:08То, что Вам удалось (?) "подсунуть" двумерный массив - недокументированная особенность.
В сети это используется и описано. И разработчик, реализуя эту возможность, исходил из чего-то...
https://www.debugpoint.com/2014/10/range-processing-using-macro-in-libreoffice-calc-part-1/
Дело в том, что у массива массивов внутренние размерности не заданы и позволяют создавать неравномерные зубчатые (jagged) массивы. У двумерного массива размерность всегда одинакова (таблица или матрица, в отличие от разрежённой матрицы).
Массив массивов, к примеру, позволил бы хранить только непустые значения ячеек: (("A1", "B1", "C1"), ("B2"), ("A3", "C3")).
Мы же всегда, применительно к диапазону, оперируем двумерным массивом. Неудачный подход. Вопрос решался бы просто, если, уже умея принимать двумерный массив (setArrayData), Calc умел бы и возвращать его (getArrayData). Конечно, размер массива известен (size = rows * columns), но средствами Basic его преобразовать нельзя, не прибегая к циклам. Значит, менять способ обращения к элементам в рамках одной программы, как я и показал в примере выше.

UPD: А вообще, есть низкоуровневый конвертер описанных массивов, доступный в Basic? Нет, конечно...
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

#7
...если не считать этот  ;)
'   Declare an array of arrays

Sub Test
   Create_Array_of_Arrays(9, 2)  'matrix 10x3
End Sub

Sub Create_Array_of_Arrays(i, k)
   Dim aArray(i) As Variant
   For j = 0 To i
       aArray(j) = DimArray(k)
   Next j
REM Xray aArray
End Sub


P.S. Работать с ними можно, вот только объявить (речь о Basic only) через Dim не получится...
sokol92, может, поэтому и работает метод setDataArray в "недокументированном" режиме? Чтобы создав двухмерный массив, пользователь не был вынужден трансформировать его в массив массивов в цикле, чтобы присвоить диапазону. А вот обратно... То-то и не логично! There are Python list, tuple and so on... Тема закрыта.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community