jq:根据对象值条件递归删除对象的最简单方法
jq: easiest way to recursively remove objects based on object value condition
我想使用 jq
删除 JSON "object" 中的所有词典(我通常使用该术语来指代数组或词典)
a) 包含一个名为 "delete_me" 的键,并且
b) 键 "delete_me" 满足某些预定条件(空、非零、真等)
基本上,我要实现的逻辑是:遍历输入,在每个节点处,如果该节点不是数组或对象,则保留它并继续前进,否则保留它但从中移除条件 a) 或 b) 失败的任何子字典。
有什么建议吗?
示例输入:
{
"a": { "foo": "bar" },
"b": {
"i": {
"A": {
"i": [
{
"foo": {},
"bar": {
"delete_if_this_is_null": false,
"an_array": [],
"another_array": [
{
"delete_if_this_is_null": null,
"foo": "bar"
}
],
"etc": ""
},
"foo2": "s"
},
{
"foo": {
"an_array": [
{
"delete_if_this_is_null": "ok",
"foo":"bar",
"another_object": { "a":1 }
},
{
"delete_if_this_is_null": null,
"foo2":"bar2",
"another_object": { "a":1 },
"name": null
}
],
"an_object": {
"delete_if_this_is_null":null,
"foo3":"bar3"
}
},
"zero": 0,
"b": "b"
}
]
}
}
}
}
应该屈服,如果"delete_me"键是delete_if_this_is_null
并且预定条件是delete_if_this_is_null == null
:
{
"a": { "foo": "bar" },
"b": {
"i": {
"A": {
"i": [
{
"foo": {},
"bar": {
"delete_if_this_is_null": false,
"an_array": [],
"another_array": [],
"etc": ""
},
"foo2": "s"
},
{
"foo": {
"an_array": [
{
"delete_if_this_is_null": "ok",
"foo":"bar",
"another_object": { "a":1 }
}
]
},
"zero": 0,
"b": "b"
}
]
}
}
}
}
更新:这是解决方案:假设输入在文件 'input.json': 中
jq '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;
def mapper(f):
if type == "array" then map(f)
elif type == "object" then
. as $in
| reduce keys[] as $key
({};
[$in[$key] | f ] as $value
| if $value | length == 0 then .
else . + {($key): $value[0]} end)
else .
end;
walk( mapper(select((type == "object" and .delete_if_this_is_null == null) | not)) )' < input.json
我不确定你在问题中到底想完成什么,但我假设你想递归搜索 json 响应并删除 json 满足某些条件的对象条件。
您可以借助 walk
过滤器轻松完成此操作,该过滤器将在 jq 的未来版本中出现,请参阅 source.
中的实现
# 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;
有了它,您可以像这样过滤掉它们:
def filter_objects(predicate): # removes objects that satisfies some predicate
walk(
if (type == "object") and (predicate) then
empty
else
.
end
)
;
filter_objects(.delete_me) # remove objects that has a truthy property "delete_me"
Jeff 的解决方案可能耗电太多。例如,使用:
def data: [1,2, {"hello": {"delete_me": true, "a":3 }, "there": 4} ]; ];
Jeff 的解决方案是空的(即什么都没有)。
因此,以下内容可能更接近您要查找的内容:
walk(if (type == "object" and .delete_me) then del(.) else . end )
对于 data
,这会产生:
[1,2,{"hello":null,"there":4}]
备选方案
如果需要消除上述示例中的 "hello":null
的解决方案,则需要 jq 的 map_values/1 的变体。这是一种方法:
def mapper(f):
if type == "array" then map(f)
elif type == "object" then
. as $in
| reduce keys[] as $key
({};
[$in[$key] | f ] as $value
| if $value | length == 0 then .
else . + {($key): $value[0]} end)
else .
end;
data | walk( mapper(select((type == "object" and .delete_me) | not)) )
结果是:
[1,2,{"there":4}]
这是一个使用递归函数的解决方案:
def clean(condition):
if type == "object" then
if condition
then empty
else
with_entries(
if (.value|type) == "object" and (.value|condition)
then empty
else .value |= clean(condition)
end
)
end
elif type == "array" then
map(
if type == "object" and condition
then empty
else clean(condition)
end
)
else .
end
;
clean(
has("delete_if_this_is_null") and (.delete_if_this_is_null == null)
)
我想使用 jq
删除 JSON "object" 中的所有词典(我通常使用该术语来指代数组或词典)
a) 包含一个名为 "delete_me" 的键,并且 b) 键 "delete_me" 满足某些预定条件(空、非零、真等)
基本上,我要实现的逻辑是:遍历输入,在每个节点处,如果该节点不是数组或对象,则保留它并继续前进,否则保留它但从中移除条件 a) 或 b) 失败的任何子字典。
有什么建议吗?
示例输入:
{
"a": { "foo": "bar" },
"b": {
"i": {
"A": {
"i": [
{
"foo": {},
"bar": {
"delete_if_this_is_null": false,
"an_array": [],
"another_array": [
{
"delete_if_this_is_null": null,
"foo": "bar"
}
],
"etc": ""
},
"foo2": "s"
},
{
"foo": {
"an_array": [
{
"delete_if_this_is_null": "ok",
"foo":"bar",
"another_object": { "a":1 }
},
{
"delete_if_this_is_null": null,
"foo2":"bar2",
"another_object": { "a":1 },
"name": null
}
],
"an_object": {
"delete_if_this_is_null":null,
"foo3":"bar3"
}
},
"zero": 0,
"b": "b"
}
]
}
}
}
}
应该屈服,如果"delete_me"键是delete_if_this_is_null
并且预定条件是delete_if_this_is_null == null
:
{
"a": { "foo": "bar" },
"b": {
"i": {
"A": {
"i": [
{
"foo": {},
"bar": {
"delete_if_this_is_null": false,
"an_array": [],
"another_array": [],
"etc": ""
},
"foo2": "s"
},
{
"foo": {
"an_array": [
{
"delete_if_this_is_null": "ok",
"foo":"bar",
"another_object": { "a":1 }
}
]
},
"zero": 0,
"b": "b"
}
]
}
}
}
}
更新:这是解决方案:假设输入在文件 'input.json': 中
jq '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;
def mapper(f):
if type == "array" then map(f)
elif type == "object" then
. as $in
| reduce keys[] as $key
({};
[$in[$key] | f ] as $value
| if $value | length == 0 then .
else . + {($key): $value[0]} end)
else .
end;
walk( mapper(select((type == "object" and .delete_if_this_is_null == null) | not)) )' < input.json
我不确定你在问题中到底想完成什么,但我假设你想递归搜索 json 响应并删除 json 满足某些条件的对象条件。
您可以借助 walk
过滤器轻松完成此操作,该过滤器将在 jq 的未来版本中出现,请参阅 source.
# 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;
有了它,您可以像这样过滤掉它们:
def filter_objects(predicate): # removes objects that satisfies some predicate
walk(
if (type == "object") and (predicate) then
empty
else
.
end
)
;
filter_objects(.delete_me) # remove objects that has a truthy property "delete_me"
Jeff 的解决方案可能耗电太多。例如,使用:
def data: [1,2, {"hello": {"delete_me": true, "a":3 }, "there": 4} ]; ];
Jeff 的解决方案是空的(即什么都没有)。
因此,以下内容可能更接近您要查找的内容:
walk(if (type == "object" and .delete_me) then del(.) else . end )
对于 data
,这会产生:
[1,2,{"hello":null,"there":4}]
备选方案
如果需要消除上述示例中的 "hello":null
的解决方案,则需要 jq 的 map_values/1 的变体。这是一种方法:
def mapper(f):
if type == "array" then map(f)
elif type == "object" then
. as $in
| reduce keys[] as $key
({};
[$in[$key] | f ] as $value
| if $value | length == 0 then .
else . + {($key): $value[0]} end)
else .
end;
data | walk( mapper(select((type == "object" and .delete_me) | not)) )
结果是:
[1,2,{"there":4}]
这是一个使用递归函数的解决方案:
def clean(condition):
if type == "object" then
if condition
then empty
else
with_entries(
if (.value|type) == "object" and (.value|condition)
then empty
else .value |= clean(condition)
end
)
end
elif type == "array" then
map(
if type == "object" and condition
then empty
else clean(condition)
end
)
else .
end
;
clean(
has("delete_if_this_is_null") and (.delete_if_this_is_null == null)
)