jq: select 当数组中有任何值时

jq: select when any value is in array

给定输入 json

[
  {"title": "first line"},
  {"title": "second line"},
  {"title": "third line"}
]

我们如何才能仅提取包含第二个 "filter" 数组中列出的关键字的标题。例如在此处使用 shell 变量:

filter='["second", "third"]'

这种情况下的输出是

[
  {"title": "second line"},
  {"title": "third line"}
]

另外,如何使用数组过滤器取反。 例如:return 仅上一个示例中的 "first line" 条目。

有一个但是使用的是旧版本的jq。 我希望当前版本的 jq 有更多 intuitive/readable 方法可以做到这一点。

您可以结合使用 jq 和 shell 技巧,使用数组来生成过滤器。首先要生成 shell 数组,请使用 shell 中的数组符号,如下所示。请注意,以下 bash 数组的符号将 而不是 在其定义中将 , 作为分隔符。现在我们需要生成一个正则表达式过滤器来匹配字符串,所以我们生成一个交替运算符

filter=("first" "second")
echo "$(IFS="|"; echo "${filter[*]}"
first|second

您没有提到字符串是否仅在第一个或最后一个匹配,或者可能在 .title 部分的任何位置。下面的正则表达式匹配字符串中任意位置的字符串。

现在我们要在 jq 中使用此过滤器来匹配 .title 字符串,如下所示。注意使用 not 来否定结果。要提供实际匹配,请删除 |not.

部分
jq --arg re "$(IFS="|"; echo "${filter[*]}")" '[.[] | select(.title|test($re)|not)]' < json

解决涉及单词 "any" 的问题的一种方法通常是使用 jq 的 any,例如使用您的 shell 变量:

jq --argjson filter "$filter" '
  map((.title | split(" ")) as $title
      | select(any( $title[] as $t
                    | $filter[] as $kw
                    | $kw == $t )))' input.json

否定

与形式逻辑一样,您可以使用allany(结合否定)来解决否定问题。但是不要忘记,如果你使用 not, jq 的 not 是一个零元数过滤器。

jq --argjson filter "$filter" '
  map((.title | split(" ")) as $title
      | select(all( $title[] as $t
                    | $filter[] as $kw
                    | $kw != $t )))' input.json

其他方法

上面使用 "keyword matching" 因为那是问题指定的内容,但是当然可以很容易地修改上面的 jq 表达式以使用正则表达式或其他一些类型的匹配。

如果关键字列表很长,那么无疑需要更好的数组交集算法。