На днях возникла необходимость создать многомерный (более двух вложений) массив для использования в программе на 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)


  1. Alexufo
    21.11.2015 14:16
    +6

    Что то умно слишком. Проще между столбцами значений добавить столбцы с запятыми, а по краям столбцы с открывающей и закрывающей скобкой.
    image


    1. Cadog
      21.11.2015 14:42
      +1

      Для двухмерного довольно не сложно такое сделать. Если уже большей размерности массив, то уже сложнее. Хотя, наверно, можно придумать такую сетку в excel чтобы заполнить трёхмерный массив. Надо попробовать


    1. Rastishka
      21.11.2015 15:45
      +5

      Когда была аналогичная проблема, я между столбцов новые ничего не вставлял, просто в один столбец формулу писал вида =СЦЕПИТЬ('[', A1, ', ', B1, ', ', C1, '],')


      1. StreetAngel
        22.11.2015 13:32
        +1

        В OpenOffice будет так:

        =CONCATENATE("[";A1;",";B1;",";C1;"],")


  1. dMetrius
    21.11.2015 14:28
    +7

    Проще было в CVS экспортнуть и в текстовом редакторе нормально заменой/ругалярками всё привести к нужному виду.


    1. Cadog
      21.11.2015 14:43
      -3

      Не совсем представляю, как таким методом трёхмерный массив сделать.


      1. Alexufo
        21.11.2015 14:46
        +1

        Ну просто трехмерные и более массивы обычно дело не рук логики человека, а выход с какого-то автомата. Можно же экселю задать вопрос, почему это он столько лет работает с двухмерными массивами.


  1. Zibx
    21.11.2015 15:16
    +3

    1. Экспорт в 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)


  1. Mingun
    21.11.2015 15:30

    Работа напрямую с Cells очень медленная. Лучше сначала получить все значения, как массив, и работать уже с ним. Когда в институте была задача обработать какой-то большой массив данных из экселя это дало ускорение раз в 20. Правда, это лет 5 назад было, может сейчас что и поменялось.


    1. Cadog
      21.11.2015 15:41
      +1

      Нет, не изменилось. И сейчас медленно. Мысль хорошая. Возьму на заметку


  1. 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));
    }
    


  1. Borz
    21.11.2015 17:31
    +4

    что в этом посте есть на/для Java, что вы его в этот хаб положили?


    1. Cadog
      21.11.2015 19:23
      -2

      Если изменить скобки, то можно и для java использовать.


      1. NekitoSP
        21.11.2015 22:28
        +4

        С такой логикой ваш пост можно в хабы большинства языков программирования положить.


      1. Borz
        21.11.2015 23:58

        не java-friendly будет. Проще тогда сразу через POI скормить файл. Ловите код


        1. Borz
          22.11.2015 00:21

          да, кстати, колонка «k» избыточна. Или она добавляет ещё один уровень вложенности?


          1. Cadog
            22.11.2015 12:16

            В моём примере создаётся трёхмерный массив. k — это колонка индексов для третьего уровня вложенности.


  1. volanddd
    21.11.2015 22:27

    Хм…
    А я всегда делаю в Gedit Ctrl+h \n ),\n(