如何使用 jq 根据元素属性值过滤对象数组?

我喜欢使用 JQ过滤 json 文件:

jq . some.json

假设 json 包含一个对象数组:

{
"theList": [
{
"id": 1,
"name": "Horst"
},
{
"id": 2,
"name": "Fritz"
},
{
"id": 3,
"name": "Walter"
},
{
"id": 4,
"name": "Gerhart"
},
{
"id": 5,
"name": "Harmut"
}
]
}

我希望过滤这个列表,以便只显示 id 值为2和4的元素,因此预期的输出是:

{
"id": 2,
"name": "Fritz"
},
{
"id": 4,
"name": "Gerhart"
}

如何使用 jq 过滤 json?我已经尝试了精选和地图,但是没有一个能够起作用,例如:

$ jq '.theList[] | select(.id == 2) or select(.id == 4)' array.json
true
59777 次浏览

来自文件:

jq '.[] | select(.id == "second")'

输入 [{"id": "first", "val": 1}, {"id": "second", "val": 2}]

输出 {"id": "second", "val": 2}

我认为你可以这样做:

jq '.theList[] | select(.id == 2 or .id == 4)' array.json

可以在 map中使用 select

.theList | map(select(.id == (2, 4)))

或者更紧凑一些:

[ .theList[] | select(.id == (2, 4)) ]

虽然这样写有点低效,因为对于每个被比较的值,表达式都是重复的。这样写会更有效率,可能也更易读:

[ .theList[] | select(any(2, 4; . == .id)) ]

下面是一个使用 指数的解决方案:

.theList | [ .[map(.id)|indices(2,4)[]] ]

在这里使用 select(.id == (2, 4))通常效率不高(见下文)。

如果 jq 有 IN/1,那么它可以用来实现更高效的解决方案:

.theList[] | select( .id | IN(2,3))

如果 jq 没有 IN/1,那么可以定义如下:

def IN(s): first(select(s == .)) // false;

效率

看到效率低下的一种方法是使用 debug。例如,下面的表达式导致10个对 debug的调用,而实际上只需要9个相等性检查:

.theList[] | select( (.id == (2,3)) | debug )


["DEBUG:",false]
["DEBUG:",false]
["DEBUG:",true]
{
"id": 2,
"name": "Fritz"
}
["DEBUG:",false]
["DEBUG:",false]
["DEBUG:",true]
{
"id": 3,
"name": "Walter"
}
["DEBUG:",false]
["DEBUG:",false]
["DEBUG:",false]
["DEBUG:",false]

索引/1

原则上,使用 index/1应该是高效的,但是在撰写本文时(2017年10月) ,它的实现虽然很快(它是用 C 编写的) ,但是效率低下。