如何使用jq检查数组中是不是存在元素
Posted
技术标签:
【中文标题】如何使用jq检查数组中是不是存在元素【英文标题】:How to check if element exists in array with jq如何使用jq检查数组中是否存在元素 【发布时间】:2017-09-01 17:49:39 【问题描述】:我有一个数组,我需要检查该数组中是否存在元素,或者使用以下方法从数组中获取该元素 jq,fruit.json:
"fruit": [
"apple",
"orange",
"pomegranate",
"apricot",
"mango"
]
cat fruit.json | jq '.fruit .apple'
没用
【问题讨论】:
【参考方案1】:它在这里工作:
jq -r '.fruit | index( "orange" )' fruit.json | tail -n 1
它只获取输出的最后一行。
如果存在,则返回0
。
如果没有,则返回null
。
【讨论】:
【参考方案2】:在此处扩展答案,如果您需要过滤水果数组针对另一个水果数组,您可以执行以下操作:
cat food.json | jq '[.fruit[] as $fruits | (["banana", "apple"] | contains([$fruits])) as $results | $fruits | select($results)]'
这将在上述示例 json 中返回一个仅包含“apple”的数组。
【讨论】:
【参考方案3】:如果数组fruit
包含"apple"
,则让jq
返回成功,否则返回错误:
jq -e '.fruit|any(. == "apple")' fruit.json >/dev/null
要输出找到的元素,请更改为
jq -e '.fruit[]|select(. == "apple")' fruit.json
如果搜索固定字符串,这不是很相关,但如果 select
表达式可能匹配不同的值,例如如果是正则表达式。
要仅输出不同的值,请将结果传递给unique
。
jq '[.fruit[]|select(match("^app"))]|unique' fruit.json
将搜索所有以app
开头的水果,并输出唯一值。 (请注意,原始表达式必须包含在 []
中才能传递给 unique
。)
【讨论】:
关于最后一句(“...找到的元素...”),注意如果 .fruit 是一个包含 N 个“apple”副本的数组,那么过滤器将产生 N 个输出。 @peak 我怀疑打印输出只有在使用将匹配不同值的select
表达式时才有意义,并且fruit
数组只包含唯一值开始,但足够公平,我已经详细说明了如何输出不同值的答案。
使用a[]|select(cond)
形式测试数组中的元素是否满足条件本质上是低效的,除非在找到满足条件的元素后使用某种机制来终止搜索。使用any/2
可能是最简单的方法。
@peak 好点,我切换到any
是为了检查是否存在的最简单情况。对于调用者想要显示所有匹配条目的更高级的情况,仍然需要select
。【参考方案4】:
“包含”的语义一点也不简单。一般来说,最好使用'index'来测试一个数组是否有一个特定的值,例如
.fruit | index( "orange" )
但是,如果感兴趣的项目本身是一个数组,则一般形式:
ARRAY | index( [ITEM] )
应该使用,例如:
[1, [2], 3] | index( [[2]] ) #=> 1
输入/1
如果您的 jq 有 IN/1
,那么更好的解决方案是使用它:
.fruit as $f | "orange" | IN($f[])
如果您的 jq 有 first/1
(如 jq 1.5 一样),那么这里有一个快速定义的 IN/1
可以使用:
def IN(s): first((s == .) // empty) // false;
any(_;_)
另一个有时更方便的有效替代方法是使用any/2
,例如
any(.fruit[]; . == "orange")
或等效:
any(.fruit[] == "orange"; .)
【讨论】:
如何避免出现此错误?cat fruit.json | jq '.fruit as $f | "orange" | in($f[])'
=> jq: error (at <stdin>:9): Cannot check whether string has a string key
。我在 jq 1.5.1 上。
它似乎不适用于内置的in()
(如果它对您有用,您使用的是什么版本?)。它确实适用于您发布的自定义IN(s)
:cat fruit.json | jq 'def IN(s): . as $in | first(if (s == $in) then true else empty end) ; .fruit as $f | "ap" | IN($f[])'
=> true
请告诉我有一种更简洁的方法可以做到这一点(适用于字符串值,并且不将子字符串计为匹配项)。这些听起来很简单的函数(in
、has
、inside
、contains
——只有index
,就像你建议的那样!)的语义都不是简单的……至少当涉及到如此相似的东西时就像检查一个数组是否包含一个字符串...
FWIW,这个解决方案似乎没有在 1.6 中添加了定义(或者至少它似乎在 jqplay.org 上)。对于看似常见的用例来说,它仍然是一个相当长的语法。
我很困惑,假设查找函数时不区分大小写,而这个IN
与in
相同,但事实并非如此。 IN
是一个 "SQL-style operator,",与 in
不同,后者的语义非常混乱。【参考方案5】:
[警告:查看评论和替代答案。]
cat fruit.json | jq '.fruit | contains(["orange"])'
【讨论】:
另外,contains
只要求"orange"
是数组元素的子字符串。【参考方案6】:
对于未来的访问者,如果您碰巧有一个变量中的数组并想要检查 输入 ,并且您有 jq 1.5(没有 IN),那么您最好的选择是 index
但有第二个变量:
.inputField as $inputValue | $storedArray|index($inputValue)
这在功能上等同于.inputField | IN($storedArray[])
。
【讨论】:
以上是关于如何使用jq检查数组中是不是存在元素的主要内容,如果未能解决你的问题,请参考以下文章