Analogue for sql join in jq

2019-07-18 03:52发布

I have the following json:

[
  {"id": "1", "type": "folder", "title": "folder-1"},
  {"id": "2", "type": "folder", "title": "folder-2"},
  {"id": "3", "type": "item", "title": "item-1", "folder": "1"},
  {"id": "4", "type": "item", "title": "item-2", "folder": "2"},
  {"id": "5", "type": "item", "title": "item-3"}
]

Basically, I need to produce this output using jq, which is similar to the result of sql join:

[
  {"type": "item", "title": "item-1", "folder": "folder-1"},
  {"type": "item", "title": "item-2", "folder": "folder-2"},
  {"type": "item", "title": "item-3"}
]

Any ideas?

标签: shell unix jq
3条回答
你好瞎i
2楼-- · 2019-07-18 04:16

Try this filter:

map(
    (select(.type=="item") | { key: .folder, value: { type, title } }),
    (select(.type=="folder") | { key: .id, value: { folder: .title } })
)
| group_by(.key)
| map(
    (map(select(.key != null) | .value) | add)
    // map(.value)[]
)

You'll have to break this out into steps.

  1. Get the items and folders and for each, take the values you're interested in and assign it a key to associate with.

    map(
        (select(.type=="item") | { key: .folder, value: { type, title } }),
        (select(.type=="folder") | { key: .id, value: { folder: .title } })
    )
    
  2. Group all by the key

    | group_by(.key)
    
  3. Then combine the values that have keys (folders) and the value otherwise

    | map(
        (map(select(.key != null) | .value) | add)
        // map(.value)[]
    )
    
查看更多
ら.Afraid
3楼-- · 2019-07-18 04:19
jq 'map(select(has("folder") or (.["title"] | startswith("item"))) | del(.id))' sample_file

Output:

[
  {
    "type": "item",
    "title": "item-1",
    "folder": "1"
  },
  {
    "type": "item",
    "title": "item-2",
    "folder": "2"
  },
  {
    "type": "item",
    "title": "item-3"
  }
]
查看更多
Melony?
4楼-- · 2019-07-18 04:19

Here is another solution which works by separating the data into two objects $folders and $items and then constructing the desired result.

  (
    reduce map(select(.type == "folder"))[] as $f (
      {}
    ; .[$f.id] = $f
    )
  ) as $folders

| (
    reduce map(select(.type == "item"))[] as $i (
      {}
    ; .[$i.id] = $i
    ) 
  ) as $items

| [
      $items[]
    | {type, title, folder}
    | if .folder == null then del(.folder) else .folder = $folders[.folder].title end
  ]

If your version of jq has INDEX/2

def INDEX(stream; idx_expr):
  reduce stream as $row ({};
    .[$row|idx_expr|
      if type != "string" then tojson
      else .
      end] |= $row);

this can be simplified to

  INDEX(.[] | select(.type == "folder"); .id) as $folders
| INDEX(.[] | select(.type == "item"); .id) as $items
| [
      $items[]
    | {type, title, folder}
    | if .folder == null then del(.folder) else .folder = $folders[.folder].title end
  ]
查看更多
登录 后发表回答