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

Jon*_*Jon 8 select json key subset jq

给定来自数组的键的输入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'
Run Code Online (Sandbox Code Playgroud)

任何想法如何清理这个?

我觉得从嵌套的JSON对象中提取选定的属性与jq是一个很好的起点,但我无法让它工作.

mmo*_*oya 10

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

$ echo '{"1":"a","2":"b","3":"c"}' | jq '{"1", "3"}'
{
  "1": "a",
  "3": "c"
}
Run Code Online (Sandbox Code Playgroud)

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

$ echo '{"key1":"a","key2":"b","key3":"c"}' | jq '{key1, key3}'
{
  "key1": "a",
  "key3": "c"
}
Run Code Online (Sandbox Code Playgroud)

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

$ echo '{"1":"a","2":"b","3":"c"}' | jq '{"1", "3", "4"}'
{
  "1": "a",
  "3": "c",
  "4": null
}
Run Code Online (Sandbox Code Playgroud)

但这些可以被过滤掉:

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

虽然这个答案没有收到OP要求的有效输入json数组,但我发现它对于过滤您知道存在的一些键很有用。

示例用例:从 JWT获取aud和获取。iss以下内容非常简洁:

echo "jwt-as-json" | jq '{aud, iss}'
Run Code Online (Sandbox Code Playgroud)


Jef*_*ado 8

您可以使用此过滤器:

with_entries(
    select(
        .key as $k | any($keys | fromjson[]; . == $k)
    )
)
Run Code Online (Sandbox Code Playgroud)


use*_*998 8

内部检查的解决方案:

jq 'with_entries(select([.key] | inside(["key1", "key2"])))'
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,这将返回以“key1”或“key2”开头的任何条目,“key1234”和“key22”也是如此。 (4认同)

Jon*_*Jon 7

这是一些额外的说明

对于输入对象,{"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[])
          )
       )'
Run Code Online (Sandbox Code Playgroud)

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

这是语句中的内部对象with_entries

[
  {
    "key": "key1",
    "value": 1
  },
  {
    "key": "key2",
    "value": 2
  },
  {
    "key": "key3",
    "value": 3
  }
]
Run Code Online (Sandbox Code Playgroud)

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

这就是奇迹发生的地方......看看这个命令中间发生了什么。以下命令采用扩展的值数组并将它们转换为我们可以从中选择的对象列表。

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

这将产生以下结果

{"key":"key1","value":1}
{"key":"key3","value":3}
Run Code Online (Sandbox Code Playgroud)

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

def with_entries(f): to_entries|map(f)|from_entries;
Run Code Online (Sandbox Code Playgroud)

这与

def with_entries(f): [to_entries[] | f] | from_entries;
Run Code Online (Sandbox Code Playgroud)

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

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

jq -cn '1,2,3| . == (1,1,3)'
true
true
false
false
false
false
false
false
true
Run Code Online (Sandbox Code Playgroud)

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

jq -cn '1,2,3| select(. == (1,1,3))'
1
1
3
Run Code Online (Sandbox Code Playgroud)


pea*_*eak 7

杰夫的答案有一些不必要的低效率,假设--argjson keys使用 代替,这两个问题都可以通过以下方法解决--arg keys

with_entries( select( .key as $k | $keys | index($k) ) )
Run Code Online (Sandbox Code Playgroud)

更好的是,如果你的 jq 有IN

with_entries(select(.key | IN($keys[])))
Run Code Online (Sandbox Code Playgroud)


use*_*998 5

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

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

jq 'with_entries(select(.key | in({"key2":1, "key12":1})))'
Run Code Online (Sandbox Code Playgroud)

因为 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"])))'
Run Code Online (Sandbox Code Playgroud)

检查项目.key是否included?来自数组