jq: выбор подмножества ключей из объекта

Учитывая входную строку json ключей из массива, верните объект только с записями, которые имели ключи в исходном объекте и во входном массиве.

У меня есть решение, но я думаю, что оно не элегантно ({($k):$input[$k]} кажется особенно неуклюжим...) и что это шанс для меня научиться.

jq -n '{"1":"a","2":"b","3":"c"}'   \
    | jq --arg keys '["1","3","4"]' \
    '. as $input 
     | ( $keys | fromjson )
     | map( . as $k
          | $input
          | select(has($k))
          | {($k):$input[$k]}
          )
     | add'

Любые идеи, как очистить это?

Мне кажется, что извлечение выбранных свойств из вложенного объекта JSON с помощью jq — хорошая отправная точка, но я не могу заставить ее работать.


person Jon    schedule 08.04.2015    source источник
comment
Из документации не было ясно, что я могу использовать ( ) в правой части оператора == через select(.key == ("1","3","4"))   -  person Jon    schedule 15.06.2015


Ответы (5)


решение с внутренней проверкой:

jq 'with_entries(select([.key] | inside(["key1", "key2"])))'
person user5672998    schedule 19.09.2017
comment
К вашему сведению, это возвращает любую запись, начинающуюся с key1 или key2, а также key1234 и key22. - person Elte Hupkes; 28.03.2019

Вы можете использовать этот фильтр:

with_entries(
    select(
        .key as $k | any($keys | fromjson[]; . == $k)
    )
)
person Jeff Mercado    schedule 08.04.2015

внутренний оператор работает большую часть времени; однако я только что обнаружил, что внутренний оператор имеет побочный эффект, иногда он выбирает ненужные клавиши, предположим, что ввод { "key1": val1, "key2": val2, "key12": val12 } и выбор inside(["key12"]), он выберет как "key1", так и "key12"

используйте оператор in, если нужно точное совпадение: так будут выбраны только .key2 и .key12

jq 'with_entries(select(.key | in({"key2":1, "key12":1})))'

поскольку оператор in проверяет ключ только из объекта (или индекс exists? из массива), здесь он должен быть записан в синтаксисе объекта, с нужными ключами в качестве ключей, но значения не имеют значения; использование оператора in не идеально для этой цели, я хотел бы, чтобы Javascript ES6 включал обратную версию API, которая будет реализована как встроенная jq

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

jq 'with_entries(select(.key | included(["key2", "key12"])))'

чтобы проверить элемент .key является included? из массива

person user5672998    schedule 19.09.2017
comment
Разве это не должно было быть редактированием вашего другого ответа? - person Nathan Tuggy; 19.09.2017
comment
а можно два ответа в одной теме? просто потому что есть два разных подхода - person user5672998; 22.09.2017
comment
Вы можете, но обычно вы хотите сделать оговорки к подходу в том же ответе, который предлагает его, поэтому никому не нужно читать весь Q + A, прежде чем выбрать подходящий подход. И довольно похожие подходы часто можно объединить в один ответ. - person Nathan Tuggy; 22.09.2017

Вот некоторые дополнительные разъяснения

Для объекта ввода {"key1":1, "key2":2, "key3":3} хотелось бы удалить все ключи, которых нет в наборе нужных ключей ["key1","key3","key4"]

 jq -n --argjson desired_keys '["key1","key3","key4"]'  \
       --argjson input '{"key1":1, "key2":2, "key3":3}' \
    ' $input
    | with_entries(
          select(
              .key == ($desired_keys[])
          )
       )'

with_entries преобразует {"key1":1, "key2":2, "key3":3} в следующий массив пар ключ-значение и сопоставляет оператор select с массивом, а затем превращает полученный массив обратно в объект.

Вот внутренний объект в операторе with_entries.

[
  {
    "key": "key1",
    "value": 1
  },
  {
    "key": "key2",
    "value": 2
  },
  {
    "key": "key3",
    "value": 3
  }
]

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

Вот где происходит волшебство... вот посмотрите, что происходит в середине этой команды. Следующая команда берет расширенный массив значений и превращает их в список объектов, из которых мы можем выбирать.

jq -cn '{"key":"key1","value":1}, {"key":"key2","value":2}, {"key":"key3","value":3}
      | select(.key == ("key1", "key3", "key4"))'

Это даст следующий результат

{"key":"key1","value":1}
{"key":"key3","value":3}

Команда с записями может быть немного сложной, но легко запомнить, что она использует фильтр и определяется следующим образом.

def with_entries(f): to_entries|map(f)|from_entries;

Это то же самое, что

def with_entries(f): [to_entries[] | f] | from_entries;

Другая часть вопроса, которая смущает людей, — это множественные совпадения справа от ==.

Рассмотрим следующую команду. Мы видим, что на выходе получается внешнее произведение всех левых и правых списков.

jq -cn '1,2,3| . == (1,1,3)'
true
true
false
false
false
false
false
false
true

Если этот предикат находится в операторе выбора, мы сохраняем ввод, когда предикат истинен. Обратите внимание, что здесь вы также можете дублировать входные данные.

jq -cn '1,2,3| select(. == (1,1,3))'
1
1
3
person Jon    schedule 11.04.2017

В ответе Джеффа есть пара ненужных неэффективностей, обе из которых устраняются следующим образом, при условии, что --argjson keys используется вместо --arg keys:

with_entries( select( .key as $k | $keys | index($k) ) )
person peak    schedule 11.04.2017
comment
это умно. позволяет избежать прохождения всего внешнего произведения входных и желаемых ключей. - person Jon; 11.04.2017