Выберите записи на основе нескольких значений в JQ

Я работаю с JQ, и до сих пор мне это очень нравится. Я сталкиваюсь с проблемой, которую еще не нашел решения где-либо еще, и хотел посмотреть, есть ли у сообщества способ сделать это.

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

{"author": "Gary", "text": "Blah"}
{"author": "Larry", "text": "More Blah"}
{"author": "Jerry", "text": "Yet more Blah"}
{"author": "Barry", "text": "Even more Blah"}
{"author": "Teri", "text": "Text on text on text"}
{"author": "Bob", "text": "Another thing to say"}

Теперь мы хотим выбрать строки, в которых значение author равно либо "Гэри" ИЛИ "Ларри", но нет другого случая. На самом деле у меня есть несколько тысяч имен, с которыми я проверяю, поэтому я просто указываю прямое или условное (например, cat blah.json | jq -r 'select(.author == "Gary" or .author == "Larry")') не достаточно. Я пытаюсь сделать это через inside функционировать так, но получить диалог ошибки:

cat blah.json | jq -r 'select(.author | inside(["Gary", "Larry"]))'

jq: error (at <stdin>:1): array (["Gary","La...) and string ("Gary") cannot have their containment checked

Какой будет лучший способ сделать что-то подобное?

3 ответа

Решение

inside а также contains немного странно Вот несколько простых решений:

Индекс /1

select( .author as $a | ["Gary", "Larry"] | index($a) )

любая /2

["Gary", "Larry"] as $whitelist
| select( .author as $a | any( $whitelist[]; . == $a) )

Использование словаря

Если производительность - это проблема, и если "author" - это всегда строка, то следует рассмотреть решение, аналогичное предложенному @JeffMercado. Вот вариант (для использования с параметром командной строки -n):

["Gary", "Larry"] as $whitelist
| ($whitelist | map( {(.): true} ) | add) as $dictionary
| inputs
| select($dictionary[.author])

Пользователь IRC gnomon ответил на это на канале jq следующим образом:

jq 'select([.author] | inside(["Larry", "Garry", "Jerry"]))'

Интуиция, лежащая в основе этого подхода, как заявил пользователь, была: "Буквально ваша идея, только упаковка .author как [.author] чтобы заставить его быть массивом из одного элемента так inside() будет работать над этим."Этот ответ дает желаемый результат фильтрации для серии имен, представленных в списке, как и исходный вопрос.

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

Вы можете создать набор значений до чтения ваших входных данных, а затем использовать набор для фильтрации ваших входных данных.

$ jq -n --argjson names '["Larry","Garry","Jerry"]' '
(reduce $names[] as $name ({}; .[$name] = true)) as $set
    | inputs | select($set[.author])
' blah.json
Другие вопросы по тегам