【问题标题】:exclusive or (xor) operator?异或(异或)运算符?
【发布时间】:2017-09-19 19:12:12
【问题描述】:

jq 是否有异或 (AKA xor) 运算符?我在它的文档中找不到它。

(我在后来发现在 jq 中工作并记录在案的文档中找不到其他主题。我一开始可能没有找到它们,因为我使用了糟糕的搜索词。)

我希望 jq 从输入中返回所有顶级对象,这些对象的数组属性包含两个特定值之一,但不是两个值。

例如,给定输入:

[
  {"letters": ["a", "c"]},
  {"letters": ["a", "b", "c"]},
  {"letters": ["b", "c"]}
]

我只想要"letters" 属性包含"a""b" 但不能同时包含两者的对象。

我最终使用了冗长的过滤器:

map(select(.letters//[]|((contains(["a"]) or contains(["b"])) and (contains(["a", "b"])|not))))

哪个给出了正确的输出:

[{"letters":["a","c"]},{"letters":["b","c"]}]

但这是漫长、乏味且令人头疼的维护工作。有没有更简单的方法来做到这一点?

此代码的“jq play”sn-p:https://jqplay.org/s/mwBhsYud2F

PS:即使没有比我找到的更好的解决方案,我也很乐意收到有关改进它的建设性批评。

【问题讨论】:

    标签: xor jq


    【解决方案1】:

    xor 可以很容易地定义:

    def xor($a;$b): ($a or $b) and (($a and $b)|not);
    

    contains 很棘手(除非您研究过它的微妙之处,否则最好不要使用它)。一般来说,使用index会更好:

    .letters | select( xor( index("a"); index("b") ))
    

    为了效率,如果你的jq有IN会更好:

    .letters as $a | select( xor( "a" | IN($a[]); "b" | IN($a[]) ))
    

    【讨论】:

    • 这可以稍微简化一下,因为如果 $a 和 $b 是真值,则 $a xor $b 与 $a != $b 相同。
    • 但是index 不是布尔值。并且 jq 布尔值运算符不要求参数是布尔值。
    • 是的,我意识到在测试您的解决方案并选择def xor($a;$b): ($a|not) != ($b|not) ; 时,我偶然发现了def xor($a;$b): (a|not) != (b|not) ; 也有效(注意正文中缺少$)。看来您可以在def foo($x): 内使用x$x ... ;
    • 你能详细说明一下in()吗? contains() 似乎是对此的正确测试,我可以理解可能使用 index()>=0,但使用 in() 似乎需要额外的工作。
    • @jq170727 - “def($a)”本质上是“def(a): a as $a |”的缩写。试试这个: def x($a;$b): 0 | $a + $b;定义 y($a;$b): 0 | a + b; 1 | (x(.;.), y(.;.))
    【解决方案2】:

    如果.letters 中没有重复项,这里有另一种利用数组索引的鲜为人知的方法:

    $ jq -Mc 'map(select(.letters|.[["a"]]+.[["b"]]|length==1))' data.json
    

    生产

    [{"letters":["a","c"]},{"letters":["b","c"]}]
    

    这是有效的,因为如果 $x 是数组 $x[ ["a"] ] 返回 "a"$x 内的索引。例如

    $ jq -Mnc '["a","c"][["a"]]'
    [0]
    $ jq -Mnc '["a","c"][["b"]]'
    []
    $ jq -Mnc '["a","c","a","b"][["a"]]'
    [0,2]
    

    另见 jq indices builtin(在其 implementation 中使用它)

    与更明显的方法相比,建议在一般情况下使用这可能太棘手了,如果.letters 包含重复项,那么简单的length==1 检查将不起作用。

    【讨论】:

      【解决方案3】:

      这个怎么样:如果data.json 包含您的示例数据,则命令

      $ jq -Mc '
        def xor($a;$b): $a != $b ;
        map(select(.letters|xor(contains(["a"]);contains(["b"]))))
      ' data.json
      

      生产

      [{"letters":["a","c"]},{"letters":["b","c"]}]
      

      请注意,上面使用的xor 在这里有效,但与非布尔参数一起使用并不安全。更强大的版本是:

      def xor($a;$b): ($a|not) != ($b|not) ;
      

      【讨论】:

      • 这个答案比你之前的答案好。您为什么不直接编辑您之前的答案以添加此解决方案,甚至完全替换它?在@peak 的答案和这个答案之间,你们俩都很难决定哪个应该是“接受”的答案。
      • 您应该选择您认为最有帮助的那个。总的来说,我认为peak's answer 更好。
      猜你喜欢
      • 2019-05-03
      • 2017-04-13
      • 2016-05-11
      • 1970-01-01
      • 1970-01-01
      • 2014-05-04
      • 1970-01-01
      • 2010-12-08
      • 2013-03-29
      相关资源
      最近更新 更多