使用 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 权衡。