使用 jq 在 JSON 个对象中查找公共键
Find common keys in JSON objects using jq
我试图在 Json 文件中找到所有常用键,因为我们不知道文件中键的名称。
Json 文件如下所示:
{
"DynamicKey1" : {
"foo" : 1,
"bar" : 2
},
"DynamicKey2" : {
"bar" : 3
},
"DynamicKey3" : {
"foo" : 5,
"zyx" : 5
}
}
预期结果:
{
"foo"
}
我试图在这里应用 reduce/foreach 逻辑,但我不确定如何在 jq 中编写它。感谢您的帮助!!
jq '. as $ss | reduce range(1; $ss|length) as $i ([]; . + reduce ($ss[i] | keys) as $key ([]; if $ss[$i - 1] | has($key) then . +$key else . end))' file.json
发布的 Q 中存在一些不一致之处:没有所有对象共有的键,如果查看键的 pair-wise 交集,结果将包括 "foo"和 "bar".
接下来,我将针对这两个问题给出解决方案。
在多个对象中键入
[.[] | keys_unsorted[]] | group_by(.)[] | select(length>1)[0]
所有对象中的键
这是一个使用类似方法的解决方案:
length as $length
| [.[] | keys_unsorted[]] | group_by(.)[]
| select(length==$length)
| .[0]
这里涉及到group_by/2
,使用排序实现
这是另一种方法,它依赖于 built-in 函数 keys
进行排序(重点是 ((nk ln(nk)) - n(k ln(k) )) = nk ln(n),即有 n 小类 k 项优于一大类 n*k 项):
# The intersection of an arbitrary number of sorted arrays
def intersection_of_sorted_arrays:
# intersecting/1 returns a stream
def intersecting($A;$B):
def pop:
.[0] as $i
| .[1] as $j
| if $i == ($A|length) or $j == ($B|length) then empty
elif $A[$i] == $B[$j] then $A[$i], ([$i+1, $j+1] | pop)
elif $A[$i] < $B[$j] then [$i+1, $j] | pop
else [$i, $j+1] | pop
end;
[0,0] | pop;
reduce .[1:][] as $x (.[0]; [intersecting(.; $x)]);
计算所有对象共有的键:
[.[] | keys] | intersection_of_sorted_arrays
这是一个 sort-free 和 time-efficient 的答案,它依赖于 jq 在 JSON 字典中执行查找的效率。由于键是字符串,我们可以简单地使用 "bag of words" (bow
) 的概念:
def bow(stream):
reduce stream as $word ({}; .[$word|tostring] += 1);
我们现在可以解决 "Keys common to all objects" 问题如下:
length as $length
| bow(.[] | keys_unsorted[])
| to_entries[]
| select(.value==$length).key
"Keys in more than one object" 问题也类似。
当然,要实现 time-efficiency,通常需要 space-time 权衡。
我试图在 Json 文件中找到所有常用键,因为我们不知道文件中键的名称。
Json 文件如下所示:
{
"DynamicKey1" : {
"foo" : 1,
"bar" : 2
},
"DynamicKey2" : {
"bar" : 3
},
"DynamicKey3" : {
"foo" : 5,
"zyx" : 5
}
}
预期结果:
{
"foo"
}
我试图在这里应用 reduce/foreach 逻辑,但我不确定如何在 jq 中编写它。感谢您的帮助!!
jq '. as $ss | reduce range(1; $ss|length) as $i ([]; . + reduce ($ss[i] | keys) as $key ([]; if $ss[$i - 1] | has($key) then . +$key else . end))' file.json
发布的 Q 中存在一些不一致之处:没有所有对象共有的键,如果查看键的 pair-wise 交集,结果将包括 "foo"和 "bar".
接下来,我将针对这两个问题给出解决方案。
在多个对象中键入
[.[] | keys_unsorted[]] | group_by(.)[] | select(length>1)[0]
所有对象中的键
这是一个使用类似方法的解决方案:
length as $length
| [.[] | keys_unsorted[]] | group_by(.)[]
| select(length==$length)
| .[0]
这里涉及到group_by/2
,使用排序实现
这是另一种方法,它依赖于 built-in 函数 keys
进行排序(重点是 ((nk ln(nk)) - n(k ln(k) )) = nk ln(n),即有 n 小类 k 项优于一大类 n*k 项):
# The intersection of an arbitrary number of sorted arrays
def intersection_of_sorted_arrays:
# intersecting/1 returns a stream
def intersecting($A;$B):
def pop:
.[0] as $i
| .[1] as $j
| if $i == ($A|length) or $j == ($B|length) then empty
elif $A[$i] == $B[$j] then $A[$i], ([$i+1, $j+1] | pop)
elif $A[$i] < $B[$j] then [$i+1, $j] | pop
else [$i, $j+1] | pop
end;
[0,0] | pop;
reduce .[1:][] as $x (.[0]; [intersecting(.; $x)]);
计算所有对象共有的键:
[.[] | keys] | intersection_of_sorted_arrays
这是一个 sort-free 和 time-efficient 的答案,它依赖于 jq 在 JSON 字典中执行查找的效率。由于键是字符串,我们可以简单地使用 "bag of words" (bow
) 的概念:
def bow(stream):
reduce stream as $word ({}; .[$word|tostring] += 1);
我们现在可以解决 "Keys common to all objects" 问题如下:
length as $length
| bow(.[] | keys_unsorted[])
| to_entries[]
| select(.value==$length).key
"Keys in more than one object" 问题也类似。
当然,要实现 time-efficiency,通常需要 space-time 权衡。