json jq:从对象中选择键的子集

biswetbf  于 2022-11-26  发布在  其他
关注(0)|答案(6)|浏览(132)

给定一个数组中的键的输入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是一个很好的起点,但是我不能让它工作。

a64a0gku

a64a0gku1#

内部检查解决方案:

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

abithluo2#

您可以使用此筛选器:

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

js5cn81o3#

大部分时间由内部操作人员工作;然而,我发现内部运算符有副作用,有时它选择的键不是所需的,假设输入是{ "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?

rdlzhqv9

rdlzhqv94#

以下是一些附加说明
对于输入对象{"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陈述式Map到数组上,然后将产生的数组转换回对象。
下面是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

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

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

vc6uscn95#

Jeff的答案有两个不必要的低效问题,假设使用--argjson keys而不是--arg keys,则可通过以下方式解决这两个问题:

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

如果你的jq有IN,那就更好了:

with_entries(select(.key | IN($keys[])))
56lgkhnf

56lgkhnf6#

如果确定输入数组中的所有键都存在于原始对象中,则可以使用对象构造快捷方式。

$ echo '{"1":"a","2":"b","3":"c"}' | jq '{"1", "3"}'
{
  "1": "a",
  "3": "c"
}

数字应该用引号引起来,以强制jq将它们解释为键而不是文字。如果键不像数字,则不需要引号:

$ echo '{"key1":"a","key2":"b","key3":"c"}' | jq '{key1, key3}'
{
  "key1": "a",
  "key3": "c"
}

添加一个不存在的键将产生一个空值,这不太可能是OP想要的:

$ echo '{"1":"a","2":"b","3":"c"}' | jq '{"1", "3", "4"}'
{
  "1": "a",
  "3": "c",
  "4": null
}

但是可以过滤掉那些:

$ echo '{"1":"a","2":"b","3":"c"}' | jq '{"1", "3", "4"} | with_entries(select(.value != null))'
{
  "1": "a",
  "3": "c"
}

虽然这个答案没有收到OP要求的有效输入json数组,但我发现它对于过滤一些你知道存在的键很有用。
以下是一个示例用例:从JWT中获取audiss。在这种情况下,jq '{aud, iss}'非常简洁。

相关问题