Json 通用递归jq,用于对任何级别上找到的所有对象值求和

Json 通用递归jq,用于对任何级别上找到的所有对象值求和,json,jq,Json,Jq,我看到并想知道jq中是否有一种优雅的方法,可以对一组对象执行一般递归,并对它们包含的所有值求和,从而生成一个输出对象,作为所有其他对象的和 如果在一个单独的输入对象上进行此操作更容易,而该输入对象会包装所有其他对象(以避免发出咕噜声),那么这也很好 递归一系列具有大量任意嵌套的独立对象(任意的意思是,如果通用递归可以做我想要的事情,我不想为对象层次结构的细节编写jq代码),并且 对在任何级别找到的key:value对的每个数值求和 每个对象都可以是稀疏的,但所有重合的对象关键点路径的值总和应

我看到并想知道jq中是否有一种优雅的方法,可以对一组对象执行一般递归,并对它们包含的所有值求和,从而生成一个输出对象,作为所有其他对象的和

如果在一个单独的输入对象上进行此操作更容易,而该输入对象会包装所有其他对象(以避免发出咕噜声),那么这也很好

  • 递归一系列具有大量任意嵌套的独立对象(任意的意思是,如果通用递归可以做我想要的事情,我不想为对象层次结构的细节编写jq代码),并且
  • 对在任何级别找到的key:value对的每个数值求和
每个对象都可以是稀疏的,但所有重合的对象关键点路径的值总和应位于单个输出对象层次中的同一位置

例如(在这3个对象周围环绕一个数组,如果这样做更容易的话):

会产生

{ A { B { x:3, y:4, z:3 }, C { x:3, y:3, z:8 }, t:3 }, s:8 }

TL:DR在这些对象的数组上执行
。as$dot | reduce([$dot[]| path(number)]| 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}
]
首先,我们将了解如何在两个对象之间执行此“加法合并”操作;之后,在一组对象上执行它应该很容易

我们将把函数定义为
加法合并($xs;$ys)
。首先,我们将获得每个对象上的所有路径的列表,其中包含数字,并删除重复项:

([$xs, $ys | paths(numbers)] | unique) as $paths
然后,我们将使用空对象作为初始状态,减少此路径列表,使用
setpath
将空对象中的每个路径设置为
$xs
$ys
上这些路径的值之和:

reduce $paths[] as $path ({}; setpath($path; [$xs, $ys | getpath($path)] | add))
现在,我们有了
加法\u 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))
def additive_merge: . as $dot | reduce ([$dot[] | paths(numbers)] | unique)[] as $path ({}; setpath($path; [$dot[] | getpath($path)] | add));
如果我们想在数组上运行此函数,我们可以在该函数上减少数组:

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))
def additive_merge: . as $dot | reduce ([$dot[] | paths(numbers)] | unique)[] as $path ({}; setpath($path; [$dot[] | getpath($path)] | add));
或者修改函数,使其在对象数组上工作,这实际上非常容易;只需使用点输入,将其保存到变量,并在上一个函数中使用
$xs,$ys
的任何位置对其进行解压缩:

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))
def additive_merge: . as $dot | reduce ([$dot[] | paths(numbers)] | unique)[] as $path ({}; setpath($path; [$dot[] | getpath($path)] | add));

希望这有帮助

这里有一个解决方案,它使用tostreamselectreducesetpathgetpath

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

太好了,谢谢!我这样使用它来提供一组已经生成的定期摘要,并通过对所有相关值求和将它们融合在一起
def additional|u merge($xs;$ys):将([$xs,$ys|路径(数字)]|唯一)[]减少为$path({};setpath($path;[$xs,$ys | getpath($path)]|添加];将输入减少为$f({};additive_merge(.;$f))
哦,是的,这也行!与数组版本相比,双元素版本的优势在于,您不需要事先分析整个对象数组,因此可以将其与类似于
输入的内容一起使用。伟大的用例!