【问题标题】:Reverse flatten nested arrays with jq使用 jq 反向展平嵌套数组
【发布时间】:2021-10-20 13:48:25
【问题描述】:

假设我有以下嵌套数据结构

cat nested.json
[
    {
        "a": "a",
        "b": [
            {"c": "c"}
        ]
    },
    {
        "a": "a",
        "b": [
            {"c": "c"}
        ]
    }
]

我可以这样压扁它

cat nested.json | jq '
  [. as $in | reduce paths(scalars) as $path ({};
    . + { ($path | map(tostring) | join(".")): $in | getpath($path) }
  )]
' > flat.json

cat flat.json
[
  {
    "0.a": "a",
    "0.b.0.c": "c",
    "1.a": "a",
    "1.b.0.c": "c"
  }
]

要使用jq 反转展平操作,我尝试了这个

cat flat.json | jq '
  .[0] | reduce to_entries[] as $kv ({};
    setpath($kv.key|split("."); $kv.value)
  )
'

{
  "0": {
    "a": "a",
    "b": {
      "0": {
        "c": "c"
      }
    }
  },
  "1": {
    "a": "a",
    "b": {
      "0": {
        "c": "c"
      }
    }
  }
}

但是,我想转换 setpath 参数中的数字以创建数组。这不太行,但我认为它很接近?

cat flat.json  | jq '
  def makePath($s): [split(".")[] | if (test("\\d+")) then tonumber else . end];
  .[0] | reduce to_entries[] as $kv ({}; setpath(makePath($kv.key); $kv.value))
'

jq: error (at <stdin>:8): split input and separator must be strings

想要的输出与nested.json中的原始数据相同

【问题讨论】:

  • 期望的输出是什么?可以更新吗?
  • 请添加所需的输出,您的问题还不清楚。
  • 想要的输出和nested.json中的原始数据一样
  • "想要的输出和nested.json中的原始数据是一样的"所以你根本不需要改变什么?现在更不清楚了..
  • @0stone0,从一开始就很清楚。 flat.json 是输入,nested.json 是所需的输出。

标签: json jq


【解决方案1】:

这样做不是更简单吗:

编码你的输入
jq '[path(.. | scalars) as $path | {($path | join(".")): getpath($path)}] | add' nested.json
{
  "0.a": "a",
  "0.b.0.c": "c",
  "1.a": "a",
  "1.b.0.c": "c"
}

并使用解码

jq 'reduce to_entries[] as $item (null; setpath($item.key / "." | map(tonumber? // .); $item.value))' flat.json
[
  {
    "a": "a",
    "b": [
      {
        "c": "c"
      }
    ]
  },
  {
    "a": "a",
    "b": [
      {
        "c": "c"
      }
    ]
  }
]

但是,如果您不关心编码键的特殊点表示法(例如"0.b.0.c"),您可以简单地将路径数组转换为 JSON 字符串,尽管更丑陋几乎相同的效果。此外,它将自动启用包含点(例如{"a.b":3})或看起来像数字(例如{"42":"Panic!"})的输入对象字段名称的处理。

使用 JSON 键,对您的输入进行编码

jq '[path(.. | scalars) as $path | {($path | tojson): getpath($path)}] | add' nested.json
{
  "[0,\"a\"]": "a",
  "[0,\"b\",0,\"c\"]": "c",
  "[1,\"a\"]": "a",
  "[1,\"b\",0,\"c\"]": "c"
}

并使用解码

jq 'reduce to_entries[] as $item (null; setpath($item.key | fromjson; $item.value))' flat.json
[
  {
    "a": "a",
    "b": [
      {
        "c": "c"
      }
    ]
  },
  {
    "a": "a",
    "b": [
      {
        "c": "c"
      }
    ]
  }
]

【讨论】:

  • 前两个“更简单”的过滤器对我不起作用。我得到nulljq: error (at flat.json:8): number (0) and string (".") cannot be divided。你用的是什么版本的jq?我有 jq-1.6
  • 我明白你关于“处理包含点......或看起来像数字的输入对象字段名称”的观点。对于我的特定用例,使用没有特殊字符的平面键很重要,而且我知道嵌套对象不会有带点或看起来像数字的键。
  • 我不想阻止您使用点符号,我只是想让您知道它的注意事项。至于错误:最初我有一个可行的解决方案,但后来有人建议我将 path(.. | scalars) 减少到 path(scalars) 因为据说是相同的,这最终破坏了代码。我的错。我恢复了该更改,现在应该可以正常工作了。在这里试试:Encoder, Decoder
  • 非常感谢,这行得通!我不得不稍微改变你的过滤器,所以输出总是一个数组。它使我的编组和非编组逻辑在其他地方更容易。
猜你喜欢
  • 1970-01-01
  • 2016-09-29
  • 2019-01-26
  • 2017-12-26
  • 1970-01-01
  • 1970-01-01
  • 2019-04-24
相关资源
最近更新 更多