JQ: удалить объект из нескольких массивов

Я хочу использовать jq для удаления всех объектов с заданным именем из всех массивов во входных данных. Например, удаление «Name1» из этого:

{
  "Category1": [
    {
      "name": "Name1",
      "desc": "Desc1"
    },
    {
      "name": "Name2",
      "desc": "Desc2"
    }
  ],
  "Category2": [
    {
      "name": "Name1",
      "desc": "Desc1"
    },
    {
      "name": "Name3",
      "desc": "Desc3"
    }
  ],
  "Category3": [
    {
      "name": "Name4",
      "desc": "Desc4"
    }
  ]
}

Должно получиться это:

{
  "Category1": [
    {
      "name": "Name2",
      "desc": "Desc2"
    }
  ],
  "Category2": [
    {
      "name": "Name3",
      "desc": "Desc3"
    }
  ],
  "Category3": [
    {
      "name": "Name4",
      "desc": "Desc4"
    }
  ]
}

Я мало работал с jq или JSON, и после нескольких часов гугления и экспериментов я не смог понять это. Как бы я это сделал?

Самое близкое, что мне удалось, было это:

cat input | jq 'keys[] as $k | .[$k] |= map( select( .name != "Name1"))'

Это фильтрует каждый из массивов, но возвращает результат в виде трех отдельных объектов, а это не то, что мне нужно.


person fre_ber    schedule 11.07.2016    source источник


Ответы (3)


Если структура вашего входного JSON всегда такая, как в вашем примере, попробуйте следующее:

map_values(map(select(.name != "Name1")))
person Community    schedule 11.07.2016
comment
Как я могу использовать регулярное выражение для указания условия фильтра? Например, как я могу удалить все, что содержит «1»? - person Daniel; 05.10.2016
comment

Вот решение, которое удалит все объекты с указанным именем, где бы они ни встречались. Он использует общую функцию walk/1, которая является встроенной в версии jq > 1.5 и поэтому может быть опущена, если ваш jq включает ее, но нет ничего плохого в том, чтобы включать ее избыточно, например в jq-скрипте.

# Apply f to composite entities recursively, and to atoms
def walk(f):
  . as $in
  | if type == "object" then
      reduce keys[] as $key
        ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
  elif type == "array" then map( walk(f) ) | f
  else f
  end;

walk(if type == "object" and .name == "Name1" then empty else . end)

Если вы действительно хотите удалить объекты из массивов, вы можете использовать:

walk(if type == "array" then map(select( type != "object" or .name != "Name1")) else . end)
person peak    schedule 11.07.2016
comment
Теперь я понимаю, что это, вероятно, более правильный ответ, поскольку мой вопрос был сформулирован, но версия Сантьяго была тем, что я действительно хотел. Трудно формулировать точные вопросы. :-) - person fre_ber; 12.07.2016

Вот решение, которое использует reduce и del.

reduce keys[] as $k (
  .
; del(.[$k][] | select(.name == "Name1"))
)
person jq170727    schedule 28.08.2017