Tengo una estructura json que se ve así:

{
        "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"}
                ]
        }
}

Ahora quiero saber dónde puedo encontrar zapatos: podría llegar a esta consulta jq:

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

Pero eso solo me da los camiones. Útil, pero no del todo todavía. También me gustaría saber en qué caja están.

Se me ocurrió esto, pero obviamente no es correcto:

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

La respuesta que me gustaría obtener es camión1, caja1 y camión2, caja1.

Aún mejor aún: me gustaría encontrar todos los artículos y proporcionar la información, como esta:

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

Pero eso puede estar pidiendo demasiado :)

2
Michiel 28 may. 2020 a las 17:00

3 respuestas

La mejor respuesta

Esto me parece una exageración, pero hace el trabajo.

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

Guarde el filtro jq anterior en el archivo item_in.jq y ejecútelo como jq --from-file item_in.jq. Pasar su entrada a esto da el siguiente resultado:

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

La transformación inicial fue volcar las hojas y sus caminos desde el árbol JSON de entrada. Ver https://github.com/stedolan/jq/issues/78#issuecomment- 17819519

2
pii_ke 28 may. 2020 a las 18:52

La respuesta que me gustaría obtener es camión1, caja1 y camión2, caja1

En este caso, puede obtenerlo con:

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

Que devuelve:

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

Estas son las rutas en su objeto que conducirán a un objeto cuya propiedad .item está establecida en "zapatos"

1
customcommander 28 may. 2020 a las 16:17

Aquí hay una solución genérica que no asume que los "elementos" están en matrices, o incluso que los valores asociados con las claves de "elementos" son siempre cadenas:

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)) }
'

Salida

Con su aporte:

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

Si desea la salida como un solo objeto JSON, recopile lo anterior en una matriz y use add.

2
peak 28 may. 2020 a las 17:13