【问题标题】:Extract all unique keys from arbitrarily nested json data with jq使用 jq 从任意嵌套的 json 数据中提取所有唯一键
【发布时间】:2016-05-11 15:53:12
【问题描述】:

正如题主所说,我的目标是编写一个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
;

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

【问题讨论】:

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


    【解决方案1】:

    一种天真的方法只会收集所有键并获取唯一值。

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

    但是有了这些数据,它有点慢,因为需要收集和排序键。

    我们可以在这方面做得更好。由于我们试图确定所有不同的键,我们将使用某种哈希图来提高效率。嗯,我们有可以这样操作的对象。

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

    我没有测量时间,但它比其他版本快很多。我什至没有等待另一个完成,这个在我的工作机器(i5-2400@3.1GHz)上在 10 秒内就完成了。

    【讨论】:

    • 谢谢!我完全错过了.. 运算符,没有它,除了我设计的解决方案之外,我无法想象其他任何东西。你的最后一种方法在我的机器上大约需要 14 秒,而你标记为 naive 的方法需要 19 秒。
    • 实际上,现在我意识到无论如何我都可以使用recurse(.[]?) 而不是..。我感觉到我让事情变得比他们应该做的更复杂。 :P 谢谢!
    【解决方案2】:

    我想你会发现 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);
    

    【讨论】:

    • 它确实比@jeff-mercado 提出的解决方案更快,jq-1.4-1-e73951f 附带 Ubuntu 15.10。然而,它与我刚刚从 git 存储库编译出来的 jq-1.5rc2-152-g0b82185 的 Jeff 版本一样快。谢谢你们!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多