jq选择数组不包含字符串的元素

时间:2016-09-13 09:49:52

标签: jq set-intersection

现在,这有点类似于jq: select only an array which contains element A but not element 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"
        ] } ]

我想选择包含特定字符串的所有元素,例如" 4210910 3a"或者 - 哪个更好 - 你不包含给定字符串列表的任何成员。

3 个答案:

答案 0 :(得分:2)

当涉及到可能更改输入时,您应该将其作为过滤器的参数,而不是将其硬编码。另外,使用contains可能对您不起作用。它以递归方式运行过滤器,因此即使子字符串匹配也可能不是首选。

例如:

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

true

我会这样写:

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

答案 1 :(得分:1)

这个响应解决了.ou是一个数组的问题,并给出了另一个禁用字符串数组。

为清楚起见,让我们定义一个过滤器intersectq(a;b),如果数组有一个共同的元素,它将返回true:

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

这实际上是一个循环内循环,但由于any/2的语义,一旦找到匹配,计算就会停止。(*)

假设$ ex是异常列表,那么我们可以用来解决问题的过滤器是:

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

例如,我们可以按照Jeff建议的方式使用调用:

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

现在你可能会问:为什么要使用any-within-any-double循环而不是。[] - 所有双循环?答案是效率,可以使用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),因此效率低,但这篇文章的要点是强调。[] - 所有双循环的缺点。

答案 2 :(得分:0)

此解决方案使用 foreach 包含检查输入的每个元素的.ou成员。

  ["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