jq:当任何值在数组中时选择

7

鉴于输入的json

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

我们如何提取仅包含第二个“过滤器”数组中列出的关键字的标题。例如,可以使用shell变量:

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

在这种情况下,输出将会是:
[
  {"title": "second line"},
  {"title": "third line"}
]

此外,如何使用数组过滤器否定条件。
例如:仅返回前一个示例中的“第一行”条目。
有一个类似的回答,但使用旧版本的jq。(参见此处)我希望在当前版本的jq中有更直观/可读的方法来完成这个任务。
2个回答

5
您可以使用 jq 结合 shell 技巧和数组来生成过滤器。首先,使用以下格式从 shell 中生成数组。请注意,下面的 bash 数组符号在定义时不会将 , 作为分隔符。现在,我们需要生成一个正则表达式过滤器来匹配字符串,因此我们生成一个备选操作符。
filter=("first" "second")
echo "$(IFS="|"; echo "${filter[*]}"
first|second

您没有说明字符串是仅在标题的开头,结尾还是可以出现在任何位置。下面的正则表达式匹配字符串在任何位置出现。

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

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

这里关键是使用test()。感谢您还添加了一种聪明的方法来解析shell数组并将结果作为输入传递。 - Bernard
@Alkaline - 请注意,jq的“test”基于正则表达式匹配,而原问题要求关键字匹配。通常,单纯使用“test”可能与基于字符串的关键字匹配产生非常不同的结果。 - peak

2

解决涉及“任何”一词的问题的一种方法通常是使用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

其他方法

上述内容使用了“关键词匹配”,因为这是问题所指定的,但是当然可以很容易地修改上述 jq 表达式以使用正则表达式或其他类型的匹配。

如果关键词列表非常长,则无疑需要更好的数组交集算法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接