【问题标题】:how to use jq to filter select items not in list?如何使用 jq 过滤选择不在列表中的项目?
【发布时间】:2017-06-15 08:56:56
【问题描述】:

在 jq 中,我可以很容易地在列表中选择一个项目

$ echo '["a","b","c","d","e"]' | jq '.[] | select(. == ("a","c"))'

或者,如果您更喜欢将其作为数组获取:

$ echo '["a","b","c","d","e"]' | jq 'map(select(. == ("a","c")))'

但是如何选择列表中的所有项目?当然. != ("a","c") 不起作用:

$ echo '["a","b","c","d","e"]' | jq 'map(select(. != ("a","c")))'
[
  "a",
  "b",
  "b",
  "c",
  "d",
  "d",
  "e",
  "e"
]

上面给出了每个项目两次,除了"a""c"

同样适用于:

$ echo '["a","b","c","d","e"]' | jq '.[] | select(. != ("a","c"))'
"a"
"b"
"b"
"c"
"d"
"d"
"e"
"e"

如何过滤匹配的项目?

【问题讨论】:

  • 那是残酷的痛苦,但我确实设法得到它。
  • 您的过滤器实际上与. != "a" or . != "c" 相同。这当然总是正确的,所以你没有看到任何过滤的东西。但是,由于您使用的是逗号运算符,因此您现在得到了重复。请记住,对于从逗号生成的每个值,都会使用新值重新评估表达式。所以select(. != ("a","c")) 变成了select(. != "a"), select(. != "c")。然后应该很清楚发生了什么。
  • 感谢@JeffMercado 的解释。我不知道为什么它不起作用。本质上,. != ("a","c") 是逻辑 OR,我期待的是逻辑 AND(即使 . == ("a","c") 是逻辑 OR)。
  • 并非如此。这更像是("a","c") 是两个值"a""c"。对于使用它的任何表达式,复制表达式,用值 "a""c" 替换副本。

标签: arrays json select jq blacklist


【解决方案1】:

最简单和最强大(w.r.t. jq 版本)的方法是使用内置的-

$ echo '["a","b","c","d","e"]' | jq -c '. - ["a","c"]'
["b","d","e"]

如果黑名单很长并且充满了重复,那么删除它们可能是合适的(例如使用unique)。

变化

也可以使用 indexnot 解决问题(在 jq 1.4 及更高版本中),例如

["a","c"] as $blacklist
| .[] | select( . as $in | $blacklist | index($in) | not) 

或者,使用从命令行传入的变量(jq --argjson blacklist ...):

.[] | select( . as $in | $blacklist | index($in) | not) 

要保留列表结构,可以使用map( select( ...) )

对于 jq 1.5 或更高版本,您还可以使用 anyall,例如

def except(blacklist):
  map( select( . as $in | blacklist | all(. != $in) ) );

特殊情况:字符串

参见例如Select entries based on multiple values in jq

【讨论】:

  • 你如何在这里使用any?你能分享一个例子吗?
  • 仅供参考,我所做的是:def inarray($val;ary): ary | any(. == $val); def notinarray($val;ary): ary | all(. != $val);
  • 啊哈! - 运算符!谢谢@peak。所以- 等价于“不在“a”中且不在“c”中?
  • 对于- 变体(迄今为止最简单的),如果输入是一个数组,例如[{"val":"a"},{"val":"b"},{"val":"c"},{"val":"d"},{"val":"e"}] 并且您想通过.val - ["a","c"] 过滤(这不是工作)?
  • @deitch - 我建议你创建一个新的 SO 问题。
【解决方案2】:

我确信这不是最简单的解决方案,但它确实有效:)

$ echo '["a","b","c","d","e"]' | jq '.[] | select(test("[^ac]"))'

编辑:另一种解决方案 - 这更糟糕:)

$ echo '["a","b","c","d","e"]' | jq '.[] | select(. != ("a") and . != ("b"))'

【讨论】:

  • 使用正则表达式是个好主意,但这实际上只是一个简单的示例。我正在与一系列项目进行比较。我希望它只是单个字符。
  • @deitch:你仍然可以使用test,只是用not反转结果,例如:test("^(abc|bcd)$") | not
  • @Thor 这很有趣。我可以用一个变量来做吗,例如js --arg match "abc" '.[] | select(test("^($match)$") | not ?
  • @Picard,我最初是使用您的替代解决方案完成的。问题是我有一个未知的预先匹配的项目列表。
  • 据我了解,您的初始解决方案不起作用,因为它检查输入数组中的每个字母与匹配列表中的每个项目 - 因此它将输入“a”与“a”匹配从列表中(没有!=匹配)然后是“a”和“c”(是!=匹配),所以它输出输入“a”(尽管你认为不应该这样做)。如果它是“元素集”元素类型,可能情况会有所不同,但从列表的工作方式来看,我认为没有一个简短的操作员解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-15
  • 1970-01-01
  • 1970-01-01
  • 2021-04-09
  • 2021-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多