【问题标题】:General recursive jq for summing all object values found at any level通用递归 jq 用于对在任何级别找到的所有对象值求和
【发布时间】:2016-07-18 18:00:16
【问题描述】:

我看到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 }

【问题讨论】:

    标签: json jq


    【解决方案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 将空对象中的每个路径设置为 $xs 和 @ 上这些路径的值的总和987654327@:

    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));
    

    希望这会有所帮助!

    【讨论】:

    • 太好了,谢谢!我像这样使用它来提供一组已经生成的定期摘要,并通过对所有相关值求和来将它们融合在一起。 def additive_merge($xs;$ys): reduce ([$xs, $ys | paths(numbers)] | unique)[] as $path ({}; setpath($path; [$xs, $ys | getpath($path)] | add)); reduce inputs as $f ({}; additive_merge(.; $f))
    • 哦,是的,这也有效!与数组版本相比,二元素版本的优势在于您无需事先分析整个对象数组,因此您可以将其与inputs 之类的东西一起使用。很棒的用例!
    【解决方案2】:

    这是一个使用 tostreamselectreducesetpathgetpath。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-14
      • 1970-01-01
      • 1970-01-01
      • 2017-08-02
      相关资源
      最近更新 更多