jq выберите элементы с массивом, не содержащим строки

Теперь это несколько похоже на jq: выберите только массив, который содержит элемент A, но не элемент B, но у меня это почему-то не работает (что, вероятно, моя ошибка) ... ;-)

Итак, вот что у нас есть:

[ {

        "employeeType": "student",
        "cn": "dc8aff1",
        "uid": "dc8aff1",
        "ou": [
            "4210910",
            "4210910 #Abg",
            "4210910 Abgang",
            "4240115",
            "4240115 5",
            "4240115 5\/5"
        ]
    },
    {
        "employeeType": "student",
        "cn": "160f656",
        "uid": "160f656",
        "ou": [
            "4210910",
            "4210910 3",
            "4210910 3a"
        ] } ]

Я хотел бы выбрать все элементы, в которых ou не содержит конкретную строку, скажем «4210910 3a» или - что было бы еще лучше - где ou не содержит ни одного члена данного списка строк. .


person Thomas    schedule 13.09.2016    source источник


Ответы (3)


Когда дело доходит до возможного изменения входных данных, вы должны сделать его параметром вашего фильтра, а не жестко его кодировать. Кроме того, использование contains может не сработать для вас в целом. Он запускает фильтр рекурсивно, поэтому будут совпадать даже подстроки, которые могут быть нежелательными.

Например:

["10", "20", "30", "40", "50"] | contains(["0"])

is true

Я бы написал это так:

$ jq --argjson ex '["4210910 3a"]' 'map(select(all(.ou[]; $ex[]!=.)))' input.json
person Jeff Mercado    schedule 13.09.2016

Этот ответ обращается к случаю, когда .ou - это массив, и нам дан еще один массив запрещенных строк.

Для ясности давайте определим фильтр intersectq(a;b), который будет возвращать истину, если массивы имеют общий элемент:

def intersectq(a;b):
  any(a[]; . as $x | any( b[]; . == $x) );

По сути, это цикл внутри цикла, но из-за семантики any/2 вычисление остановится, как только будет найдено совпадение. (*)

Предполагая, что $ ex - это список исключений, тогда фильтр, который мы могли бы использовать для решения проблемы, был бы следующим:

map(select(intersectq(.ou; $ex) | not))

Например, мы могли бы использовать вызов в соответствии со строками, предложенными Джеффом:

$ jq --argjson ex '["4210910 3a"]' -f myfilter.jq input.json

Теперь вы можете спросить: зачем использовать двойной цикл «любое внутри любого», а не двойной цикл. [] - внутри всего? Ответ - эффективность, как можно увидеть с помощью debug:

$ jq -n '[1,2,3] as $a | [1,1] as $b | all( $a[]; ($b[] | debug) != .)'
["DEBUG:",1]
["DEBUG:",1]
false

$ jq -n '[1,2,3] as $a | [1,1] as $b | all( $a[]; . as $x | all( $b[]; debug | $x != .))'
["DEBUG:",1]
false

(*) Сноска

Конечно, intersectq/2, как здесь определено, по-прежнему O (m * n) и, следовательно, неэффективен, но основная мысль этого поста - подчеркнуть недостаток двойного цикла. [] - within-all.

person peak    schedule 13.09.2016

Вот решение, которое проверяет член .ou каждого элемента ввода с помощью foreach и contains.

  ["4210910 3a"] as $list   # adjust as necessary

| .[]
| foreach $list[] as $e (
    .; .; if .ou | contains([$e]) then . else empty end
  )

РЕДАКТИРОВАТЬ: теперь я понимаю, что фильтр формы foreach E as $X (.; .; R) почти всегда можно переписать как E as $X | R, так что приведенное выше действительно просто

  ["4210910 3a"] as $list
| .[]
| $list[] as $e
| if .ou | contains([$e]) then . else empty end
person jq170727    schedule 06.08.2017