Как отфильтровать большую JSON с маленькой JSON с помощью jq? - PullRequest
0 голосов
/ 09 февраля 2020

У меня большой JSON, и я хочу отфильтровать содержимое с несколькими условиями, другими словами, получить указанные c элементы, которые определяют c значений.

Этот процесс основан на jq(v1.5) и bash.

json -a (просто пример):

[
   {
    "city": "abc"
   },
   {
    "city": "def"
   },
   {
    "city": "ghi"
   },
  {
    "city": "jkl"
   }
]

Теперь я хочу получить {"city": "ab c "} и {" city ":" ghi "}, но я не хочу передавать -l oop с bash (я думаю, jq - отличный инструмент с собственной функцией l oop).

Итак, я построил небольшое json, содержащее указанные c значения.

json -b (просто пример):

["abc","ghi"]

и выполнил команду jq :

$ json-b='["abc","ghi"]'
$ cat json-a | jq --argjson arg ${json-b} '.[] | select( ($arg|index(.city)) )' # return error
error: Cannot index array with string "city"

$ cat json-a | jq --argjson arg ${json-b} '.[] | select( ($arg|index( \(.city) )) )' # return error
error: compile error

# BUT
$ cat json-a | jq --argjson arg ${json} '.[] | .city as $c | select( ($arg|index($c)) )' # It's work

Итак, выходной объект не может быть вызван встроенной функцией? Я пробовал in, contains тоже не работает. Я что-то там не так сделал? Или есть способ получше?

Спасибо.

Ответы [ 3 ]

0 голосов
/ 09 февраля 2020

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

jq --argfile array json-b.json '
  map(select( .city as $city | $array | index($city)))
' json-a.json

Однако, вероятно, было бы лучше поставить программу jq в файл и вызовите jq с опцией -f:

jq -f program.jq --argfile array json-b.json json-a.json

Пояснения к неудачным попыткам

$arg|index(.city)

Причина, по которой ваша первая попытка не может работать в подразумевается, что $arg|index(.city) в точности эквивалентен $arg|index($arg.city), что явно не то, что нужно.

index( \(.city) )

Это выражение просто синтаксически неверно. Возможно, вы имели в виду интерполяцию строк; если это так, выражение будет index("\(.city)"), но это та же проблема, что и в предыдущем разделе.

0 голосов
/ 09 февраля 2020

есть ли лучший способ?

Вот решение, которое имеет ряд потенциальных преимуществ, как указано ниже:

< json-a.json jq --argfile array json-b.json '
  INDEX(.city)[$array[]]'

Обратите внимание, что сложность времени этот алгоритм приблизительно равен O (n) + O (m), где n и m - длины двух массивов. Напротив, наивная итерация по массиву объектов и проверка по массиву соответствующих городов имеет сложность по времени O (n) * O (m).

Другое преимущество состоит в том, что это решение не использует index/1 , реализация которой в настоящее время неоптимальна.

Предостережение

Если в главном массиве более одного объекта с одинаковым значением .city, то результаты могут быть или не быть такими хотел.

0 голосов
/ 09 февраля 2020

. внутри select не ссылается на строку из вашего входного массива, но $arg из-за внутренней части |

Это ваш рабочий пример

$ echo  '[{"c":"a"},{"c":"d"},{"c":"g"},{"c":"j"}]' | \
        jq '.[] | .c as $c | select( ["a","g"] | index($c) ) '
{
  "c": "a"
}
{
  "c": "g"
}

Теперь мы replace выбираем map

$ echo  '[{"c":"a"},{"c":"d"},{"c":"g"},{"c":"j"}]' | \
             jq '.[] | .c as $c | map( ["a","g"] | index($c) ) '
[
  0
]
[
  null
]
[
  1
]
[
  null
]

Теперь вместо index($c) мы заменяем .

$ echo  '[{"c":"a"},{"c":"d"},{"c":"g"},{"c":"j"}]' | \
             jq '.[] | .c as $c | map( ["a","g"] | . ) '
[
  [
    "abc",
    "ghi"
  ]
]
[
  [
    "abc",
    "ghi"
  ]
]
[
  [
    "abc",
    "ghi"
  ]
]
[
  [
    "abc",
    "ghi"
  ]
]
...