【问题标题】:How to get subtree of json containing known object如何获取包含已知对象的json子树
【发布时间】:2020-04-02 11:29:52
【问题描述】:

我想从i3-msg -t get_treejq 中提取包含焦点窗口的子树。我知道可以使用

找到焦点窗口

i3-msg -t get_tree | jq ".. | (.nodes? // empty)[] | select(.focused == true)"

一个简单的例子是:

{
  "node": [
    {
      "node": {
        "foo": "bar"
      }
    },
    {
      "node": {
        "foo": "foo"
      }
    }
  ]
}

如果搜索包含.foo == "bar"node,则输出应该返回

{
  "node": [
    {
      "node": {
        "foo": "bar"
      }
    }
  ]
}

但我似乎找不到合适的方法来提取从根跨越到该节点的子树。

【问题讨论】:

    标签: bash jq i3


    【解决方案1】:
    .node |= map(select(.node.foo == "bar"))
    

    这个概念被称为Update assignment

    【讨论】:

    • 经过一番挖掘,我发现这个解决方案在过滤掉具有非空节点的元素时产生了最好的结果i3-msg -t get_tree | jq ".. | (.nodes? // empty)[] | .nodes |= map(select(.nodes[].focused == true)) | select(.nodes != [])"
    • @vintnes - 术语“复杂分配”是指 LHS 是一个复杂术语的分配。这里 LHS 是一个简单的术语。
    • @peak 不同版本的文档差异很大;你可以随便叫它。因为你破坏了我的链接,所以我恢复了你的编辑。
    • @vintnes - 抱歉,如果我断开了链接。在文档的 v1.3、v1.4、v1.5 和 v1.6 中,“作业”部分包括 |= 小节。类似的“开发”版本(stedolan.github.io/jq/manual/#Assignment
    【解决方案2】:

    原始问题有两个不同的子问题,一个与使用 .. 相关,但未参考已发布的 JSON 示例,另一个基于特定类型的 JSON 输入。此响应使用基于使用paths 的策略,大致如下:

    reduce pv as [$p,$v] (null; setpath($p; $v))
    

    这可能会也可能不会根据需要处理数组,这部分取决于所需的内容。如果一般不需要数组中的null 值,则添加对walk/1 的调用如下是合适的:

    walk(if type == "array" then map(select(. != null)) else . end)
    

    或者,如果必须保留原始中存在的null 值,则可以使用下面附录中详述的策略。

    (1) 以使用..为特征的问题

    def pv:
     paths as $p
     | getpath($p)
     | . as $v
     | (.nodes? // empty)[] | select(.focused == true)
     | [$p,$v];
    
    reduce pv as [$p,$v] (null; setpath($p; $v))
    

    如上所述,要消除所有数组中的所有空值,您可以调用walk/1。否则,如果null 的值被setpath 插入到数组中以保留原始结构方面,请参阅下面的附录

    (2) 对于示例 JSON,以下内容就足够了:

    def pv:
     paths as $p
     | getpath($p)
     | . as $v
     | (.node? // empty) | select(.foo == "bar")
     | [$p,$v];
    
    reduce pv as [$p,$v] (null; setpath($p; $v))
    

    对于给定的样本,这会产生:

    {"node":[{"node":{"foo":"bar"}}]}
    

    对于类似的输入,如果想从数组中删除null 值,只需像以前一样添加对walk/1 的调用即可;另请参阅下面的附录

    附录

    如果不需要 setpath 可能插入到数组中的 null 值以保留原始结构,最简单的方法是将原始 JSON 中的 null 值更改为某个独特的值(例如“:null: "),执行选择,修剪 null 值,然后将独特的值转换回 null

    示例

    例如,考虑 foo/bar 示例的这个变体:

    {
      "node": [
        {
          "node": {
            "foo": "foo0"
          }
        },
        {
          "node": {
            "foo": "bar",
            "trouble": [
              null,
              1,
              null
            ]
          }
        },
        {
          "node": {
            "foo": "foo1"
          }
        },
        {
          "node": {
            "foo": "bar",
            "trouble": [
              1,
              2,
              3
            ]
          }
        }
      ],
      "nodes": [
        {
          "node": {
            "foo": "foo0"
          }
        },
        {
          "node": {
            "foo": "bar",
            "trouble": [
              null,
              1,
              null
            ]
          }
        }
      ]
    }
    

    使用“:null:”作为区别值,可以使用前面针对这种情况显示的“main”程序的以下变体:

    walk(if type == "array" then map(if . == null then ":null:" else . end) else . end)
    | reduce pv as [$p,$v] (null; setpath($p; $v))
    | walk(if type == "array"
           then map(select(. != null) | if . == ":null:" then null else . end)
           else . end)
    

    【讨论】:

    • walk/1 非常强大。一个问题:假设选定的子树具有不应删除的null 数组元素。您是否知道一种将其限制在结果的“上部”部分的简单方法,其中所有 null 数组元素都是 setpath 的结果?
    • 查看walk/1 我怀疑从getpath 计算最大深度并将其传递给在深度限制处停止的walk/2 可能会起作用。
    • @jq170727 - 请参阅更新(尤其是附录)。一种语义等效的替代方法是使用特殊版本的 setpath,它使用独特的值而不是 null,但它会复杂得多。任何其他策略要么失败,要么实施起来非常复杂。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 2020-08-06
    • 1970-01-01
    • 1970-01-01
    • 2020-12-16
    • 2021-05-06
    相关资源
    最近更新 更多