Выберите записи на основе нескольких значений в 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