使用jq查找JSON对象中的公共键

t1rydlwq  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(87)

我试图在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
bfhwhh0e

bfhwhh0e1#

Q中有一些不一致的地方:不存在所有对象共有的键,并且如果查看键的成对交叉,则结果将包括“foo”和“bar”。
在下面,我将介绍这两个问题的解决方案。

多个对象中的键

[.[] | keys_unsorted[]] | group_by(.)[] | select(length>1)[0]

所有对象中的键

下面是一个使用类似方法的解决方案:

length as $length
| [.[] | keys_unsorted[]] | group_by(.)[]
| select(length==$length) 
| .[0]

这涉及到group_by/2,它是使用排序实现的。
这里有一种替代方法,它依赖于内置函数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
t5fffqht

t5fffqht2#

下面是一个无需排序且时间高效的答案,它依赖于jq在JSON字典中实现查找的效率。由于键是字符串,我们可以简单地使用“单词袋”(bow)的概念:

def bow(stream): 
  reduce stream as $word ({}; .[$word|tostring] += 1);

我们现在可以解决“所有对象共有的密钥”问题,如下所示:

length as $length
| bow(.[] | keys_unsorted[])
| to_entries[]
| select(.value==$length).key

类似地,对于“多个对象中的键”问题也是如此。
当然,为了实现时间效率,有通常的时空权衡。

相关问题