Используйте jq для объединения JSON массивов в несколько файлов - PullRequest
0 голосов
/ 06 февраля 2020

У меня есть серия JSON файлов, содержащих массив записей, например,

$ cat f1.json
{
  "records": [
    {"a": 1},
    {"a": 3}
  ]
}

$ cat f2.json
{
  "records": [
    {"a": 2}
  ]
}

Я хочу 1) извлечь одно поле из каждой записи и 2) вывести один массив, содержащий все значения полей из всех входных файлов.

Первая часть проста:

jq '.records | map(.a)' f?.json
[
  1,
  3
]
[
  2
]

Но я не могу понять, как получить jq для объединения этих выходных массивов в один массив!

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

Ответы [ 5 ]

5 голосов
/ 06 февраля 2020

Предполагая, что ваш jq имеет inputs (что верно для jq 1.5 и более поздних версий), было бы наиболее эффективно использовать его, например, следующим образом:

jq -n '[inputs.records[].a]' f*.json
2 голосов
/ 06 февраля 2020

Использование -s (или --slurp):

jq -s 'map(.records[].a)' f?.json
2 голосов
/ 06 февраля 2020

Если ваши входные файлы велики, то из-за потери файла может потребоваться много памяти, в этом случае вы можете reduce, который работает итеративно, добавляя содержимое массива .a по одному объекту за раз

jq -n 'reduce inputs.records[].a as $d (.; . += [$d])' f?.json

Флаг -n предназначен для построения вывода JSON с нуля с использованием данных, доступных из inputs. Функция reduce принимает начальное значение ., которое из-за нулевого ввода будет просто null. Затем для каждого из входных объектов . += [$d] гарантирует, что содержимое массива .a добавляется вместе.

1 голос
/ 06 февраля 2020

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

$ jq '.records<b>[]</b> | .a' f?.json | jq -s
[
  1,
  3,
  2
]
1 голос
/ 06 февраля 2020

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

Я бы использовал следующее:

jq --slurp 'map(.records | map(.a)) | add' f?.json

Мы применяем ваш текущий преобразование к каждому элементу входного массива (ваши предыдущие отдельные входы), затем мы объединяем эти преобразованные массивы в один с add.

...