Общий рекурсивный jq для суммирования всех значений объекта, найденных на любом уровне

json jq

481 просмотра

2 ответа

208 Репутация автора

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

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

  • выполнять рекурсивный анализ по ряду независимых объектов, которые имеют много произвольных вложений (произвольно в том смысле, что мне не нужно кодировать jq для специфики иерархии объектов, если общая рекурсия может делать то, что я хочу), и
  • сумма каждого числового значения пары ключ: значение, найденного на любом уровне

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

Например (обернуть массив вокруг этих 3 объектов, если это облегчает):

{ A { B { x:1, y:4      }, C { x:3,      z:6 }, t:2 }, s:5 }
{ A { B { x:2     , z:3 },                      t:1 }      }
{ A {                    , C {      y:3, z:2 }      }, s:3 }

будет производить

{ A { B { x:3, y:4, z:3 }, C { x:3, y:3, z:8 }, t:3 }, s:8 }
Автор: redgiant Источник Размещён: 18.07.2016 06:00

Ответы (2)


1 плюс

0 Репутация автора

Решение

TL: DR; Над массивом этих объектов сделайте. as $dot | reduce ([$dot[] | paths(numbers)] | unique)[] as $path ({}; setpath($path; [$dot[] | getpath($path)] | add))


Поскольку ваш пример ввода не является допустимым JSON, вот JSON, который я использовал для тестирования этого. Дайте мне знать, если это не соответствует вашему:

[
    {"a": {"b": 1, "c": 2},         "e": 3},
    {"a": {        "c": 2, "d": 3},         "f": 4}
]

Сначала мы рассмотрим, как выполнить эту операцию «аддитивного слияния» между двумя объектами; после этого должно быть легко выполнить его над массивом объектов.

Мы определим нашу функцию как additive_merge($xs; $ys). Сначала мы получим список всех путей для каждого из этих объектов, в которых есть числа, с удаленными дубликатами:

([$xs, $ys | paths(numbers)] | unique) as $paths

Тогда мы снизим этот список путей, с пустым объектом в качестве исходного состояния, используя setpathдля установки каждого пути в пустом объекте к сумме значений этих путей на $xsи $ys:

reduce $paths[] as $path ({}; setpath($path; [$xs, $ys | getpath($path)] | add))

Теперь все вместе, у нас есть наша additive_mergeфункция:

def additive_merge($xs;$ys): reduce ([$xs, $ys | paths(numbers)] | unique)[] as $path ({}; setpath($path; [$xs, $ys | getpath($path)] | add));

Если мы хотим запустить эту функцию над массивом, мы можем уменьшить массив над этой функцией:

reduce .[] as $x ({}; additive_merge(.; $x))

Или измените функцию так, чтобы она работала над массивом объектов, что на самом деле очень просто; просто используйте точечный ввод, сохраните его в переменной и распакуйте, где бы он ни $xs, $ysиспользовался в предыдущей функции:

def additive_merge: . as $dot | reduce ([$dot[] | paths(numbers)] | unique)[] as $path ({}; setpath($path; [$dot[] | getpath($path)] | add));

Надеюсь это поможет!

Автор: user3899165 Размещён: 18.07.2016 07:58

0 плюса

5975 Репутация автора

Вот решение , которое использует tostream , выберите , уменьшить , setpath и GetPath .

reduce (tostream|select(length==2)|.[0] = .[0][1:]) as [$p,$v] (
   {};
   setpath($p; getpath($p) + $v)
)
Автор: jq170727 Размещён: 06.08.2017 01:07
Вопросы из категории :
32x32