使用jq

时间:2016-05-11 15:53:13

标签: json parsing scripting command-line-interface jq

正如主题所述,我的目标是编写一个all_keys函数从任意嵌套的json blob中提取所有键,根据需要遍历包含的数组和对象,并输出包含键的数组,而不重复。

例如,给出以下输入:

[
    {"name": "/", "children": [
      {"name": "/bin", "children": [
        {"name": "/bin/ls", "children": []},
        {"name": "/bin/sh", "children": []}]},
      {"name": "/home", "children": [
        {"name": "/home/stephen", "children": [
          {"name": "/home/stephen/jq", "children": []}]}]}]},
    {"name": "/", "children": [
      {"name": "/bin", "children": [
        {"name": "/bin/ls", "children": []},
        {"name": "/bin/sh", "children": []}]},
      {"name": "/home", "children": [
        {"name": "/home/stephen", "children": [
          {"name": "/home/stephen/jq", "children": []}]}]}]}      
]

all_keys功能应产生此输出:

[
  "children",
  "name"
]

为此,我设计了以下功能,但它很复杂,所以我想知道你是否能想出一种更简洁,更快捷的方法来获得相同的结果。

def all_keys: 
    . as $in |
    if type == "object" then 
        reduce keys[] as $k (
            [];
            . + [$k, ($in[$k] | all_keys)[]]
        ) | unique
    elif type == "array" then (
        reduce .[] as $i (
            [];
            . + ($i | all_keys)
        ) | unique
    ) 
    else
        empty
    end
;

作为参考,在this 53MB json file上运行该功能大约需要22秒才能使用我的英特尔T9300@2.50GHz CPU(我知道,它很古老,但仍能正常工作)。

2 个答案:

答案 0 :(得分:3)

一种天真的方法只会收集所有密钥并获得唯一值。

[.. | objects | keys[]] | unique

但是有了这些数据,由于需要收集和分类密钥,所以它有点慢。

我们可以做得更好一些。由于我们正在尝试确定所有不同的密钥,因此我们使用某种类型的散列图来提高效率。好吧,我们有对象可以这样做。

reduce (.. | objects | keys[]) as $k ({}; .[$k] = true) | keys

我没有测量时间,但它的速度比其他版本快。我甚至没有等到另一个完成,我的工作机器(i5-2400@3.1GHz)在10秒内就完好了。

答案 1 :(得分:1)

我认为你会发现OP的all_keys的以下变体实际上比使用..的版本略快;这可能是预期的 - 对于jeopardy.json,..共生成1,731,807个JSON实体,而只有216,930个JSON对象:

def all_keys:
  def uniquely(f): reduce f as $x ({}; .[$x] = true) | keys;
  def rkeys:
    if type == "object" then keys[] as $k | ($k, (.[$k]|rkeys))
    elif type == "array" then .[]|rkeys
    else empty
    end;
  uniquely(rkeys);