jq — Как выбирать объекты на основе «белого списка» значений свойств

Поскольку пример стоит тысячи слов, скажем, у меня есть следующий поток JSON:

{"a": 0, "b": 1}
{"a": 2, "b": 2}
{"a": 7, "b": null}
{"a": 3, "b": 7}

Как я могу сохранить все объекты, для которых свойство .b является одним из [1, 7] (на самом деле список намного длиннее, поэтому я не хочу делать select(.b == 1 or .b == 7)). Я ищу что-то вроде этого: select(.b in [1, 7]), но я не смог найти то, что ищу на странице руководства.


person foo    schedule 19.01.2016    source источник


Ответы (2)


Выполнение $value in $collection может быть достигнуто с помощью шаблона select($value == $collection[]). Более эффективной альтернативой будет select(any($value == $collection[]; .)) Итак, ваш фильтр должен быть таким:

[1, 7] as $whitelist | select(any(.b == $whitelist[]; .))

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

$ jq --argjson whitelist '[2, 7]' 'select(any(.b == $whitelist[]; .))'
person Jeff Mercado    schedule 19.01.2016
comment
Исходный вопрос относился к потоку, а не к массиву, и в этом случае вам не нужно было бы использовать map. - person ; 19.01.2016
comment
Также обратите внимание, что [1, 7][] может быть выражено как (1, 7). - person ; 19.01.2016
comment
Первоначально в вопросе указывалось, что ввод был массивом, но это было изменено после того, как я ответил. - person Jeff Mercado; 19.01.2016
comment
Да, я плохо отредактировал вопрос, как только увидел ваш ответ, я просто хотел сделать вопрос более целенаправленным. @SantiagoLapresta, где документация по конструкции (1, 7), я не могу найти ее на странице руководства. - person foo; 20.01.2016
comment
@foo: Суть в том, что с запятыми выражение дает несколько значений. Когда вы используете [] в массиве, это выражение также дает несколько значений. Я предполагаю, что соответствующий раздел в руководстве находится на [,]. [1, 2, 3][] эквивалентно 1, 2, 3. - person Jeff Mercado; 20.01.2016

Следующий подход с использованием index/1 подобен тому, что искали изначально (".b в [1, 7]"), и может быть заметно быстрее, чем использование .[] внутри select, если белый список большой.

Если ваш jq поддерживает --argjson:

jq --argjson w '[1,7]' '. as $in | select($w | index($in.b))'

В противном случае:

jq --arg w '[1,7]' '. as $in | ($w|fromjson) as $w | select($w | index($in.b))'

or:

jq '. as $in | select([1, 7] | index($in.b))'

ОБНОВИТЬ

30 января 2017 года была добавлена ​​встроенная функция IN для эффективной проверки наличия объекта JSON в потоке. Его также можно использовать для эффективного тестирования членства в массиве. Например, приведенный выше вызов с параметром --argjson можно упростить до:

jq --argjson w '[1,7]' 'select( .b | IN($w[]) )'

Если у вашего jq нет IN/1, то пока у вашего jq есть first/1, вы можете использовать это эквивалентное определение:

def IN(s): . as $in | first(if (s == $in) then true else empty end) // false;
person peak    schedule 19.01.2016