Объединение json массивов из нескольких файлов с использованием jq, группировка по ключу - PullRequest
3 голосов
/ 05 августа 2020

Я хотел бы объединить два или более файлов в один json и объединить массивы под одним ключом.

file1. json

{
  "shapes": [
    {
      "id": "1",
      "name": "circle"
    },
    {
      "id": "2",
      "name": "square"
    }
  ]
}

file2. json

{
  "shapes": [
    {
      "id": "3",
      "name": "triangle"
    }
  ]
}

Ожидаемый результат:

{
  "shapes": [
    {
      "id": "1",
      "name": "circle"
    },
    {
      "id": "2",
      "name": "square"
    },
    {
      "id": "3",
      "name": "triangle"
    }
  ]
}

Я могу сделать это с помощью следующей команды jq:

jq -s '{shapes: map(.shapes)|add }' file*.json

Но для этого мне нужно знать shape атрибут и жестко закодируйте его. Есть ли простой способ получить тот же результат без явного использования имени ключа?

Ответы [ 3 ]

1 голос
/ 05 августа 2020

Вот решение, которое также решает более общую проблему: во-первых, оно обрабатывает произвольно много входных файлов; и, во-вторых, он формирует «сумму» по ключам для каждого ключа, исходя из предположения, что каждый ключ верхнего уровня имеет значение массива.

Общая c функция:

  # the values at each key are assumed to be arrays
  def aggregate(stream): 
    reduce stream as $o ({}; 
      reduce ($o|keys_unsorted[]) as $k (.; 
        .[$k] += $o[$k] ));

Чтобы избежать «хлюпания», мы будем использовать inputs:

aggregate(inputs)

Поэтому при вызове необходимо использовать параметр командной строки -n:

jq -n -f program.jq *.json
1 голос
/ 10 августа 2020

Вот решение, которое подходит, когда каждый объект верхнего уровня имеет только один ключ, и это эффективно и концептуально просто. Предполагается, что jq вызывается с параметром -n.

reduce inputs as $in (null;
   ($in|keys_unsorted[0]) as $k | { ($k): (.[$k] + $in[$k]) })

или немного более компактно:

reduce inputs as $in (null; ($in|keys_unsorted[0]) as $k | .[$k] += $in[$k] )
0 голосов
/ 05 августа 2020

Попробуйте следующий код. Это может обрабатывать любое количество файлов. Предполагается, что все входные данные являются объектами json со всеми значениями внутри в виде массивов. Все такие массивы объединяются после группировки по ключам. Он выводит объект, ключи которого связаны с соответствующими агрегированными массивами.

jq -s 'map(to_entries)|add|group_by(.key)|
    map( { "key": (.[0].key), "value": (map(.value)|add)})|
    from_entries' file1.json file2.json

Для вашего образца ввода это дает:

{
  "shapes": [
    {
      "id": "1",
      "name": "circle"
    },
    {
      "id": "2",
      "name": "square"
    },
    {
      "id": "3",
      "name": "triangle"
    }
  ]
}
...