I'm trying to find all common keys in a Json file, given that we don't know names of keys in the file.
the Json file looks like:
{
"DynamicKey1" : {
"foo" : 1,
"bar" : 2
},
"DynamicKey2" : {
"bar" : 3
},
"DynamicKey3" : {
"foo" : 5,
"zyx" : 5
}
}
Expect result:
{
"foo"
}
I was trying to apply reduce/foreach logic here but I am not sure how to write it in jq. I appreciate any help!!
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
There are some inconsistencies in the Q as posted: there are no keys common to all the objects, and if one looks at the pair-wise intersection of keys, the result would include both "foo" and "bar".
In the following, I'll present solutions for both these problems.
[.[] | keys_unsorted[]] | group_by(.)[] | select(length>1)[0]
Here's a solution using a similar approach:
length as $length
| [.[] | keys_unsorted[]] | group_by(.)[]
| select(length==$length)
| .[0]
This involves group_by/2
, which is implemented using a sort.
Here is an alternative approach that relies on the built-in function keys
to do the sorting (the point being that ((nk ln(nk)) - n(k ln(k))) = nk ln(n), i.e. having n small sorts of k items is better than one large sort of n*k items):
# 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)]);
To compute the keys common to all the objects:
[.[] | keys] | intersection_of_sorted_arrays