【问题标题】:jq filter: ok if item not in listjq 过滤器:如果项目不在列表中,则确定
【发布时间】:2019-11-15 11:26:21
【问题描述】:

(这里是jq新手,对不起,如果这个问题有一个明显的答案:))

我想根据一个值是否在列表中不是来过滤json。

这是一个具体的例子:

输入

[ 
    {
        "n": "A",
        "a": 659533330984,
        "vals": {
            "n2": "B",
            "b": 5193941030
        }
     },
    {
        "n": "A",
        "a": 659533330984,
        "vals": {
            "n2": "C",
            "b": 4872891707
        }
    },
    {
        "n": "B",
        "a": 659533330984,
        "vals": {
            "n2": "C",
            "b": 4872891707
        }
    }
]

过滤器

[.n, .vals.n2]不在(["A", "B"], ["B", "C"])

因此,在 jq 中,我尝试了以下命令(同样基于 this related question

jq '[ .[] | select([.n, .vals.n2] as $i | (["A", "B"], ["B", "C"]) | index($i) | not )]'

jq '[ .[] | select([.n, .vals.n2] != (["A", "B"], ["B", "C"]))]'

但是,这两个命令都给出了输出

[
  {
    "n": "A",
    "a": 659533330984,
    "vals": {
      "n2": "B",
      "b": 5193941030
    }
  },
  {
    "n": "A",
    "a": 659533330984,
    "vals": {
      "n2": "C",
      "b": 4872891707
    }
  },
  {
    "n": "A",
    "a": 659533330984,
    "vals": {
      "n2": "C",
      "b": 4872891707
    }
  },
  {
    "n": "B",
    "a": 659533330984,
    "vals": {
      "n2": "C",
      "b": 4872891707
    }
  }
]

而这将是所需的输出——没有重复并且所有“黑名单”值的逻辑与:

[
  {
    "n": "A",
    "a": 659533330984,
    "vals": {
      "n2": "C",
      "b": 4872891707
    }
  }
]

第二个命令不起作用是有道理的,因为如果我理解正确,逗号运算符基本上意味着 jq 为每个列出的元素计算一次表达式 - 因此重复。然而,简单地通过unique 管道并没有帮助,因为输出不应包含 任何 过滤器对。

我目前唯一的另一个想法是通过选择通过选择管道选择...对于“黑名单”中的每个项目。但是,我想将黑名单作为输入读取——我可以动态创建命令,但我想知道是否有更漂亮的解决方案?感觉好像一定有​​……

我很高兴听到您对如何以最佳方式处理此问题的意见。

我使用的是 jq 版本 jq-1.5-1-a5b5cbe。

【问题讨论】:

    标签: arrays json jq indexof


    【解决方案1】:

    表达式:

    ([.n, .vals.n2]) not in (["A", "B"], ["B", "C"])
    

    相当于:

    ([.n, .vals.n2]) != ["A", "B"] and ([.n, .vals.n2]) != ["B", "C"]
    

    正如你在这里:

    select([.n, .vals.n2] != (["A", "B"], ["B", "C"]))
    

    它并不完全相同,因为逗号实际上使它成为or

    你需要做更多这样的事情:

    select([.n, .vals.n2] as $v | $v != ["A", "B"] and $v != ["B", "C"])
    

    select([.n, .vals.n2] as $v | all(["A", "B"], ["B", "C"]; $v != .))
    

    此外,如果您想坚持第一种方法,则必须将值放入数组中,而不仅仅是用逗号分隔。

    select([.n, .vals.n2] as $i | [["A", "B"], ["B", "C"]] | index($i) | not)
    

    【讨论】:

    • 非常感谢您的有用回复!仅供参考,对我来说,您提到的最后一个命令返回输入(未过滤):jqplay.org/s/yUztANtOe5倒数第二个命令有效:)
    【解决方案2】:

    当使用index 查找数组的索引(比如$x)时,你必须这样写:

    index([$x])
    

    (这与 index 旨在以统一的方式处理 JSON 字符串和数组这一事实有关。)

    有效的解决方案

    [["A", "B"], ["B", "C"]] as $blacklist
    | map( [.n, .vals.n2] as $i
           | select( $blacklist | index([$i]) | not) )
    

    来自jq FAQ

    ?:给定一个数组 A,其中包含一个项 X,我如何找到 X 在 A 中的最小索引?为什么 [1] | index(1) 返回 null 而不是 0?为什么[1,2] | index([1,2]) 返回 0 而不是 null?

    答:在数组中求X的最小索引的最简单统一方法是查询[X]而不是X本身,即:index([X])。

    相比之下,过滤器 index([1,2]) 尝试查找 [1,2] 作为输入数组中连续项的子序列。这是为了与 t | 的行为保持一致。索引,其中 s 和 t 是字符串。

    如果 X 不是数组,则 index([X]) 可以缩写为 index(X)。

    【讨论】:

    • 非常感谢您的回复!也许我做错了什么,对我来说这没有给出预期的结果:jqplay.org/s/_ffcIjiTj3
    • 那里的逻辑是错误的。将迭代移出select
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 2020-05-05
    • 1970-01-01
    相关资源
    最近更新 更多