一般递归jq,用于求和在任何级别找到的所有对象值

时间:2016-07-18 18:00:17

标签: json jq

我看到summing value in an array of maps并想知道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 }

2 个答案:

答案 0 :(得分:1)

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将空对象中的每个路径设置为{{1上的那些路径的值的总和}和$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

希望这有帮助!

答案 1 :(得分:0)

以下是使用 tostream 选择缩小 setpath getpath <的解决方案/强>

reduce (tostream|select(length==2)|.[0] = .[0][1:]) as [$p,$v] (
   {};
   setpath($p; getpath($p) + $v)
)