使用jq递归减少数组
recursive reduce arrays using jq
如何递归查找对象中的所有数组并将它们缩减为第一项?
我尝试使用 if .[0]? == "" then .[0] else . end
检测数组,但如果当前对象不是数组,它不会输出任何内容。
输入:
{
"a": 1,
"b": [
1,
2,
3
],
"c": [
{
"a": 1,
"b": [
1,
2,
3
],
"c": {
"a": 1,
"b": [
1,
2,
3
]
}
},
{
"a": 1,
"b": [
1,
2,
3
],
"c": {
"a": 1,
"b": [
1,
2,
3
]
}
},
{
"a": 1,
"b": [
1,
2,
3
],
"c": {
"a": 1,
"b": [
1,
2,
3
]
}
}
]
}
输出:
{
"a": 1,
"b": [
1
],
"c": [
{
"a": 1,
"b": [
1
],
"c": {
"a": 1,
"b": [
1
]
}
}
]
}
walk/1 包含在最近 (post-1.5) 的 jq 版本中。它也可以在下面找到。
根据我的理解,它可以用来实现您的 objective:
walk(if type == "array" and length > 1 then [.[0]] else . end)
# Apply f to composite entities recursively, and to atoms
def walk(f):
. as $in
| if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
elif type == "array" then map( walk(f) ) | f
else f
end;
peak 的回答很棒。这个问题也可以使用递归来解决,但是如何让它工作还不是很明显。
我想出的天真的解决方案是 (.. | arrays) |= .[0]
,但这不起作用,因为递归是从外部完成的,这意味着在嵌套情况下我们最终会尝试获取 .[0]来自不再是数组的值。
这可以通过执行 post 顺序遍历来解决,如 in this GitHub issue 所述。
使用 post_recurse
,只需将上面的简单解决方案中的 ..
替换为 post_recurse
即可。完整解决方案:
def post_recurse(f): def r: (f | select(. != null) | r), .; r; def post_recurse: post_recurse(.[]?); (post_recurse | arrays) |= .[0]
这是一个使用 tostream 的解决方案,它转换输入对象
进入路径流,过滤掉任何具有非零数组索引的路径
并使用 reduce 和 setpath 将结果转换回对象。所有递归都在 tostream.
内部
[
tostream
| if length != 2 then empty
elif ([.[0][]|numbers|.!=0]|any) then empty
else .
end
]
| reduce .[] as $p (
{};
setpath($p[0]; $p[1])
)
如何递归查找对象中的所有数组并将它们缩减为第一项?
我尝试使用 if .[0]? == "" then .[0] else . end
检测数组,但如果当前对象不是数组,它不会输出任何内容。
输入:
{
"a": 1,
"b": [
1,
2,
3
],
"c": [
{
"a": 1,
"b": [
1,
2,
3
],
"c": {
"a": 1,
"b": [
1,
2,
3
]
}
},
{
"a": 1,
"b": [
1,
2,
3
],
"c": {
"a": 1,
"b": [
1,
2,
3
]
}
},
{
"a": 1,
"b": [
1,
2,
3
],
"c": {
"a": 1,
"b": [
1,
2,
3
]
}
}
]
}
输出:
{
"a": 1,
"b": [
1
],
"c": [
{
"a": 1,
"b": [
1
],
"c": {
"a": 1,
"b": [
1
]
}
}
]
}
walk/1 包含在最近 (post-1.5) 的 jq 版本中。它也可以在下面找到。
根据我的理解,它可以用来实现您的 objective:
walk(if type == "array" and length > 1 then [.[0]] else . end)
# Apply f to composite entities recursively, and to atoms
def walk(f):
. as $in
| if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
elif type == "array" then map( walk(f) ) | f
else f
end;
peak 的回答很棒。这个问题也可以使用递归来解决,但是如何让它工作还不是很明显。
我想出的天真的解决方案是 (.. | arrays) |= .[0]
,但这不起作用,因为递归是从外部完成的,这意味着在嵌套情况下我们最终会尝试获取 .[0]来自不再是数组的值。
这可以通过执行 post 顺序遍历来解决,如 in this GitHub issue 所述。
使用 post_recurse
,只需将上面的简单解决方案中的 ..
替换为 post_recurse
即可。完整解决方案:
def post_recurse(f): def r: (f | select(. != null) | r), .; r; def post_recurse: post_recurse(.[]?); (post_recurse | arrays) |= .[0]
这是一个使用 tostream 的解决方案,它转换输入对象 进入路径流,过滤掉任何具有非零数组索引的路径 并使用 reduce 和 setpath 将结果转换回对象。所有递归都在 tostream.
内部[
tostream
| if length != 2 then empty
elif ([.[0][]|numbers|.!=0]|any) then empty
else .
end
]
| reduce .[] as $p (
{};
setpath($p[0]; $p[1])
)