На днях возникла необходимость создать многомерный (более двух вложений) массив для использования в программе на JavaScrip. Одно из обязательных условий, что массив должен быть объявлен непосредственно в коде функции. Значений он (массив) будет содержать порядка 2000 — 3000. В общем, перспектива набивание «забора» из квадратных скобок и запятых в несколько тысяч штук приводила в уныние.
К моему счастью, данные для массива уже были заготовлены в определенном формате, который позволял экспортировать их в текстовый файл или табличный редактор MS Excel. Так возникла идея автоматизации этого рутинного процесса.
Да, существует большое количество конвертеров, позволяющих генерировать массивы из CSV данных. Но применимы они только для создания двумерных массивов (Или я не нашел таких. Буду благодарен за подсказку). Мне же необходим массив с вложенностью до трех уровней. В итоге я принялся за реализацию своего конвертера с применением VBA макросов в MS Excel. Мне показалось, что MS Excel может использоваться как удобный инструмент для формирования многомерных массивов. Гораздо удобнее подготовить данные в табличном виде с возможностью автоматизированного заполнения диапазонов.
Первые три колонки A, B, C — это индексы массива, обозначающие уровни вложенности. Колонка D — значения для массива. Результат выводится в ячейку F1.
Код VBA функции
Sub create_array()
Set ws1 = ThisWorkbook.Sheets("Лист1")
lines_amount = 2025
Dim i As Integer, max1 As Integer, max2 As Integer, max3 As Integer, j As Integer, k As Integer, y As Integer, index As Integer
max1 = 0
For i = 2 To lines_amount - 1
If max1 < CInt(ws1.Cells(i, 1).Value) Then
max1 = CInt(ws1.Cells(i, 1).Value)
End If
Next i
ws1.Cells(1, 6).Value = "["
For i = 1 To max1
max2 = 0
For index = 2 To lines_amount - 1
If max2 < CInt(ws1.Cells(index, 2).Value) And CInt(ws1.Cells(index, 1).Value) = i Then
max2 = CInt(ws1.Cells(index, 2).Value)
End If
Next index
ws1.Cells(1, 6).Value = ws1.Cells(1, 6).Value & "["
For j = 1 To max2
max3 = 0
For index = 2 To lines_amount - 1
If max3 < CInt(ws1.Cells(index,3).Value) And CInt(ws1.Cells(index,1).Value)=i And CInt(ws1.Cells(index,2).Value)=j Then
max3 = CInt(ws1.Cells(index, 3).Value)
End If
Next index
ws1.Cells(1, 6).Value = ws1.Cells(1, 6).Value & "["
For k = 1 To max3
For y = 2 To lines_amount - 1
If CInt(ws1.Cells(y,1).Value)=i And CInt(ws1.Cells(y,2).Value)=j And CInt(ws1.Cells(y,3).Value)=k Then
ws1.Cells(1, 6).Value = ws1.Cells(1, 6).Value & ws1.Cells(y, 4).Value
ws1.Rows(y).Delete
Exit For
End If
Next y
If k <> max3 Then
ws1.Cells(1, 6).Value = ws1.Cells(1, 6).Value & ","
End If
Next k
If j <> max2 Then
ws1.Cells(1, 6).Value = ws1.Cells(1, 6).Value & "],"
Else
ws1.Cells(1, 6).Value = ws1.Cells(1, 6).Value & "]"
End If
Next j
If i <> max1 Then
ws1.Cells(1, 6).Value = ws1.Cells(1, 6).Value & "],"
Else
ws1.Cells(1, 6).Value = ws1.Cells(1, 6).Value & "]"
End If
Next i
ws1.Cells(1, 6).Value = ws1.Cells(1, 6).Value & "]"
End Sub
Через переменную lines_amount = 2025 задается количество строк для обработки. Первой строкой является заголовок, поэтому диапазон данных начинается со второй строки index = 2.
В моем случае индексы шли не по порядку, поэтому функция просматривает весь диапазон несколько раз. В случае ручного формирования таблицы с данными, думаю, код можно упростить. Из за нескольких итераций просмотра всего диапазона с данными, выполнение функции может занимать большое количество времени. В связи с чем в функцию добавлен код для удаления строки из таблицы, которая уже занесена в массив. Поэтому настоятельно рекомендую копировать лист с данными перед выполнением функции.
Код VBA функции ни в коем случае не претендует на роль идеального инструмента. Нужно рассматривать его как прототип, демонстрирующий возможность использования таблиц MS Excel для подготовки массивов.
Пример книги Excel (необходимо выполнить макрос Вид -> Макросы -> create_array)
https://cloud.mail.ru/public/7mpU/s1mNTmuzh
Комментарии (18)
dMetrius
21.11.2015 14:28+7Проще было в CVS экспортнуть и в текстовом редакторе нормально заменой/ругалярками всё привести к нужному виду.
Cadog
21.11.2015 14:43-3Не совсем представляю, как таким методом трёхмерный массив сделать.
Alexufo
21.11.2015 14:46+1Ну просто трехмерные и более массивы обычно дело не рук логики человека, а выход с какого-то автомата. Можно же экселю задать вопрос, почему это он столько лет работает с двухмерными массивами.
Zibx
21.11.2015 15:16+31. Экспорт в csv
2. Пожирание данных на js в нужную структуру. Как я понял, в этом случае это цикл по всем строкам от 1 и до конца и какой-то простейший код:
if(!arr[i]) arr[i] = []; if(!arr[i][j]) arr[i][j] = []; arr[i][j][k] = data;
* предварительно нужно получить i, j, k, data из csv файла для каждой строки.
3. Получение чистых данных в нужной структуре через, если так удобнее через JSON.stringify(data, null, 2)
Mingun
21.11.2015 15:30Работа напрямую с Cells очень медленная. Лучше сначала получить все значения, как массив, и работать уже с ним. Когда в институте была задача обработать какой-то большой массив данных из экселя это дало ускорение раз в 20. Правда, это лет 5 назад было, может сейчас что и поменялось.
movl
21.11.2015 16:13+1Лично мне лень было бы даже CSV парсить, и я люблю js, так что как по мне, самое простое решение экспортировать табличку в Google SpreadSheets, и написать небольшой скриптик
Кодfunction makeArray() { var result = [] , ss = SpreadsheetApp.getActiveSpreadsheet() , sheet = ss.getActiveSheet() , range = sheet.getRange('A:D') , values = range.getValues() ; for (var c = 0; c < values.length; c++) { if (values[c][0] === '') break; var i = values[c][0] , j = values[c][1] , k = values[c][2] , v = values[c][3] ; if (typeof result[i] === "undefined") result[i] = []; if (typeof result[i][j] === "undefined") result[i][j] = []; result[i][j][k] = v; } Browser.msgBox(JSON.stringify(result)); }
Alexufo
Что то умно слишком. Проще между столбцами значений добавить столбцы с запятыми, а по краям столбцы с открывающей и закрывающей скобкой.
Cadog
Для двухмерного довольно не сложно такое сделать. Если уже большей размерности массив, то уже сложнее. Хотя, наверно, можно придумать такую сетку в excel чтобы заполнить трёхмерный массив. Надо попробовать
Rastishka
Когда была аналогичная проблема, я между столбцов новые ничего не вставлял, просто в один столбец формулу писал вида =СЦЕПИТЬ('[', A1, ', ', B1, ', ', C1, '],')
StreetAngel
В OpenOffice будет так:
=CONCATENATE("[";A1;",";B1;",";C1;"],")