Как я могу сопоставить элементы вместе в структуре json с помощью jq? - PullRequest
2 голосов
/ 28 мая 2020

У меня есть структура json, которая выглядит следующим образом:

{
        "lorry1": {
                "box1": [
                        {"item": "shoes", "state": "new"},
                        {"item": "snacks", "state": "new"}
                ],
                "box2": [
                        {"item": "beer", "state": "cold"},
                        {"item": "potatoes"}
                ]
        },
        "lorry2": {
                "box1": [
                        {"item": "shoes", "state": "new"},
                        {"item": "snacks", "state": "new"}
                ],
                "box2": [
                        {"item": "beer", "state": "lukewarm"}
                ]
        }
}

Теперь я хочу знать, где я могу найти обувь: я мог бы придумать этот запрос jq:

to_entries | select(.[].value | .[][].item=="shoes") | map({"lorry": "\(.key)"  })

Но это дает мне только грузовики. Полезно, но пока не совсем. Я бы тоже хотел узнать, в какой коробке они находятся.

Я придумал это, но это явно неверно:

to_entries | select(.[].value | .[][].item=="shoes") | keys as $box |map({"lorry": "\(.key)", "box": $box })

Ответ, который я хотел бы get is lorry1, box1 и lorry2, box1.

Еще лучше: я хотел бы найти все предметы и предоставить информацию, например:

"shoes": [ {"lorry1", "box1"}, {"lorry2", "box1" } ],
"snacks": [ {"lorry1", "box1"}, {"lorry2", "box1"} ],
"beer": [ {"lorry1", "box2"}, {"lorry2", "box2"} ],
"potatoes": [ {"lorry1", "box2"} ]

, но это может быть спрос многовато :)

Ответы [ 3 ]

2 голосов
/ 28 мая 2020

Я хотел бы получить ответ: lorry1, box1 и lorry2, box1

В этом случае вы можете получить его с помощью:

path(.. | select(.item? == "shoes"))

Что возвращает:

["lorry1","box1",0]
["lorry2","box1",0]

Это пути в вашем объекте, которые приведут к объекту, для которого свойство .item имеет значение «shoes»

2 голосов
/ 28 мая 2020

Вот общее c решение, которое не предполагает, что «элементы» находятся в массивах или даже что значения, связанные с ключами «item», всегда являются строками:

jq -c '. as $in
  | [paths as $p | select($p[-1] == "item") | $p]
  | group_by(. as $p | $in|getpath($p))
  | .[]
  | (.[0] as $p | $in | getpath($p)) as $v
  | {($v|tostring):  ( map(.[:-1] | if .[-1] | type == "number" then .[:-1] else . end)) }
'

Вывод

С вашим вводом:

{"beer":[["lorry1","box2"],["lorry2","box2"]]}
{"potatoes":[["lorry1","box2"]]}
{"shoes":[["lorry1","box1"],["lorry2","box1"]]}
{"snacks":[["lorry1","box1"],["lorry2","box1"]]}

Если вы хотите, чтобы вывод был как один объект JSON, тогда соберите указанное выше в массив и используйте add.

2 голосов
/ 28 мая 2020

Мне это кажется излишним, но он выполняет свою работу.

[path(.[][][].item) as $p | [$p, getpath($p)]] |
group_by( .[1] ) |
map({(.[0][1]): (. | map([.[0][0,1]]))})|
add

Сохраните указанный выше фильтр jq в файле item_in.jq и запустите его как jq --from-file item_in.jq. Передача в него входных данных дает следующий результат:

{
  "beer": [
    [
      "lorry1",
      "box2"
    ],
    [
      "lorry2",
      "box2"
    ]
  ],
  "potatoes": [
    [
      "lorry1",
      "box2"
    ]
  ],
  "shoes": [
    [
      "lorry1",
      "box1"
    ],
    [
      "lorry2",
      "box1"
    ]
  ],
  "snacks": [
    [
      "lorry1",
      "box1"
    ],
    [
      "lorry2",
      "box1"
    ]
  ]
}

Первоначальное преобразование заключалось в выгрузке листьев и их путей из входного дерева JSON. См. https://github.com/stedolan/jq/issues/78#issuecomment -17819519

...