jqネストされた折りたたみ/ JSONデータのグループ化

jqネストされた折りたたみ/ JSONデータのグループ化

私は大規模なJSONデータセット(1 GB以上)で作業しており、同様の配列オブジェクトをマージしてから、ネストされた類似オブジェクトをマージする必要があります。まず、次のような行があります。

[{"item": "item1", "attributes":[{"type": "itemtype", "colour": ["blue"]}]},
{"item": "item1", "attributes":[{"type": "itemtype", "colour": ["grey"]}]},
{"item": "item2", "attributes":[{"type": "itemtype", "colour": ["blue"]}]},
{"item": "item2", "attributes":[{"type": "itemtype2", "colour": ["orange"]}]},
{"item": "item2", "attributes":[{"type": "itemtype2", "colour": ["blue"]}]},
{"item": "item3", "attributes":[{"type": "itemtype", "colour": ["blue"]}]}]

私はjqを使ってグループ化し、コードできれいに印刷しました。

 jq 'group_by(.item) | map({"item": .[0].item, "attributes": map(.attributes[])})

プロジェクトごとにグループ化し、プロパティを並べ替えます。

[
  {
    "item": "item1",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      },
      {
        "type": "itemtype",
        "colour": [
          "grey"
        ]
      }
    ]
  },
  {
    "item": "item2",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      },
      {
        "type": "itemtype2",
        "colour": [
          "orange"
        ]
      },
      {
        "type": "itemtype2",
        "colour": [
          "blue"
        ]
      }
    ]
  },
  {
    "item": "item3",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      }
    ]
  }
]

私の課題は、これらの入れ子になった属性を一緒にグループ化し、型別にグループ化し、型に応じて配列に色を追加することです。たとえば、次のようなものがあります。

[
  {
    "item": "item1",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue",
          "grey"
        ]
      }
    ]
  },
  {
    "item": "item2",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      },
      {
        "type": "itemtype2",
        "colour": [
          "orange",
          "blue"
        ]
      }
    ]
  },
  {
    "item": "item3",
    "attributes": [
      {
        "type": "itemtype",
        "colour": [
          "blue"
        ]
      }
    ]
  }
]

よりよく理解するために、LodashまたはJMESPathのオンラインエディタを試してみましたが、map()そこに他のエディタを追加しようとしましたが、進歩はmap(.attributes[])ありませんでした。どこかにReduce()を追加する必要があるようですが、私は知りません。

ありがとうございます!

答え1

この質問をお寄せいただきありがとうございます。一緒に働いて楽しかったです。


以下は何もないバリエーションですreduce

group_by(.item) |
map({
    item: first.item,
    attributes: (
        group_by(.attributes[].type) |
        map({
            type: first.attributes[].type,
            colour: ( map(.attributes[].colour[]) )
        })
    )
})

まず、元の要素をキーにグループ化しますitem。これは私たちにグループごとの配列を提供しますitem。この配列の各要素には同じitem

最初は、map()各グループのオブジェクトを作成し、グループを1つにまとめます。オブジェクトにはキーitemattributesキーがあり、キー値はitemグループの最初の要素からランダムに取得されます(すべて同じ)。

さらに、キーの値を生成group_by()します。今回は、ソースオブジェクトの配列のキーがグループ化され、生成されたグループごとにソースオブジェクトから合計値が収集されます。map()attributestypeattributestypecolour


以下を使用してこれを実行することもできますreduce

group_by(.item) |
map(reduce .[] as $a ({}; .item = $a.item | .attributes += $a.attributes)) |
map(.attributes |= (
    group_by(.type) |
    map(reduce .[] as $a ({}; .type = $a.type | .colour += $a.colour))
))

これはgroup_by()、結果の外部構造を作成することに関連しています。つまり、map(reduce)データを複数の部分にグループ化し、外部構造に従って構成することです。配列itemの値はattributes単に渡されます。

次に、各グループの配列に対してこのパターン(+)を繰り返し、group_by()値に基づいてグループ化して構成します。map(reduce)attributestype


パフォーマンスの観点から小さい入力の場合、上記の2つの解決策は互いに似ていますが、reduce問題のサイズ(約64KB)より150倍小さい入力の場合、2番目のバリエーション(使用)はわずかに高速です。これらのサイズ入力の場合、私のシステムで実行するのに約70ミリ秒かかります。これは無視できるランタイムです。

入力が大きくなるとreduced。入力サイズが4 MBの場合、最初のコードは約800ミリ秒かかりますが、2番目のコードは単一の実行に25秒かかります。

より大きな入力で実行するのが難しい場合jq(線形外挿法によると、1 GBのデータセットを実行するのに約54時間かかります)以外の方法(おそらくデータベースまたは少なくとも職場)でデータを処理することを検討できます。フォーマット)。

たとえば、与えられたデータをCSVに変換できます。

item,type,colour
item1,itemtype,blue
item1,itemtype,grey
item2,itemtype,blue
item2,itemtype2,orange
item2,itemtype2,blue
item3,itemtype,blue

...または同等のデータベーステーブルに移動してそこで作業します。

ミラーを例に挙げましょう。

$ mlr --csv nest --ivar ';' -f colour file.csv
item,type,colour
item1,itemtype,blue;grey
item2,itemtype,blue
item2,itemtype2,orange;blue
item3,itemtype,blue

関連情報