【问题标题】:jq: selecting a subset of keys from an objectjq:从对象中选择键的子集
【发布时间】:2015-06-13 14:48:27
【问题描述】:

给定一个来自数组的输入 json 字符串,返回一个对象,其中仅包含原始对象和输入数组中具有键的条目。

我有一个解决方案,但我认为它不够优雅({($k):$input[$k]} 感觉特别笨拙...),这是我学习的机会。

jq -n '{"1":"a","2":"b","3":"c"}'   \
    | jq --arg keys '["1","3","4"]' \
    '. as $input 
     | ( $keys | fromjson )
     | map( . as $k
          | $input
          | select(has($k))
          | {($k):$input[$k]}
          )
     | add'

任何想法如何清理这个?

我觉得Extracting selected properties from a nested JSON object with jq 是一个很好的起点,但我无法让它发挥作用。

【问题讨论】:

  • 从文档中不清楚我可以通过select(.key == ("1","3","4"))在 == 语句的右轴上使用 ( )

标签: json select key subset jq


【解决方案1】:

你可以使用这个过滤器:

with_entries(
    select(
        .key as $k | any($keys | fromjson[]; . == $k)
    )
)

【讨论】:

    【解决方案2】:

    inside 操作符大部分时间都在工作;但是,我刚刚发现 inside 运算符有副作用,有时它选择了不需要的键,假设输入是 { "key1": val1, "key2": val2, "key12": val12 } 并选择 inside(["key12"]) 它将同时选择 "key1""key12"

    如果需要完全匹配,请使用 in 运算符:这样将仅选择 .key2.key12

    jq 'with_entries(select(.key | in({"key2":1, "key12":1})))'
    

    因为 in 运算符仅检查对象中的键(或数组中的索引 exists?),所以这里它必须以对象语法编写,将所需的键作为键,但值无关紧要; in 运算符的使用不是一个完美的目的,我希望看到 Javascript ES6 包含 API 的反向版本,以实现 jq 内置

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

    jq 'with_entries(select(.key | included(["key2", "key12"])))'
    

    检查 .key 是否是数组中的 included?

    【讨论】:

    • 这不应该是对您其他答案的编辑吗?
    • 但是我可以在同一个线程上有两个答案吗?只是因为有两种不同的方法
    • 您可以,但通常您希望在建议的同一答案中对方法提出警告,因此在决定合适的方法之前,无需阅读整个 Q+A。相当相似的方法通常可以组合成一个答案。
    【解决方案3】:

    内部检查解决方案:

    jq 'with_entries(select([.key] | inside(["key1", "key2"])))'
    

    【讨论】:

    • 仅供参考,这将返回以 key1key2 开头的任何条目,key1234key22 也是如此。
    【解决方案4】:

    Jeff 的回答有几个不必要的低效率,假设使用--argjson keys 而不是--arg keys,以下两个问题都得到解决:

    with_entries( select( .key as $k | $keys | index($k) ) )
    

    【讨论】:

    • 这很聪明。避免遍历输入键和所需键的整个外部乘积。
    【解决方案5】:

    这里有一些额外的说明

    对于输入对象{"key1":1, "key2":2, "key3":3},我想删除所有不在所需键集合中的键["key1","key3","key4"]

     jq -n --argjson desired_keys '["key1","key3","key4"]'  \
           --argjson input '{"key1":1, "key2":2, "key3":3}' \
        ' $input
        | with_entries(
              select(
                  .key == ($desired_keys[])
              )
           )'
    

    with_entries{"key1":1, "key2":2, "key3":3} 转换为以下键值对数组,并将 select 语句映射到数组上,然后将结果数组转换回对象。

    这是with_entries 语句中的内部对象。

    [
      {
        "key": "key1",
        "value": 1
      },
      {
        "key": "key2",
        "value": 2
      },
      {
        "key": "key3",
        "value": 3
      }
    ]
    

    然后我们可以从这个数组中选择符合我们标准的键。

    这就是魔法发生的地方......这里是这个命令中间发生的事情。以下命令采用扩展的值数组并将它们转换为我们可以从中选择的对象列表。

    jq -cn '{"key":"key1","value":1}, {"key":"key2","value":2}, {"key":"key3","value":3}
          | select(.key == ("key1", "key3", "key4"))'
    

    这将产生以下结果

    {"key":"key1","value":1}
    {"key":"key3","value":3}
    

    with entries 命令可能有点棘手,但它很容易记住它需要一个过滤器并且定义如下

    def with_entries(f): to_entries|map(f)|from_entries;
    

    这个和

    一样
    def with_entries(f): [to_entries[] | f] | from_entries;
    

    另一个让人困惑的问题是==右侧的多个匹配项

    考虑以下命令。我们看到输出是所有左侧列表和右侧列表的外部生成。

    jq -cn '1,2,3| . == (1,1,3)'
    true
    true
    false
    false
    false
    false
    false
    false
    true
    

    如果该谓词在 select 语句中,我们会在谓词为真时保留输入。请注意,您也可以在此处复制输入。

    jq -cn '1,2,3| select(. == (1,1,3))'
    1
    1
    3
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-10
      • 2017-04-12
      • 2018-09-07
      • 1970-01-01
      • 1970-01-01
      • 2018-04-30
      相关资源
      最近更新 更多