查找两个数组之间的常见和唯一项

时间:2016-12-16 13:11:41

标签: arrays json jq set-intersection set-difference

我使用带有ansible的ec2.py动态库存脚本来提取ec2主机及其标签名称的列表。它返回一个JSON列表,如下所示,

  "tag_aws_autoscaling_groupName_asg_test": [
    "aa.b.bb.55",
    "1b.b.c.d"
  ],

  "tag_aws_autoscaling_groupName_asg_unknown": [
    "aa.b.bb.55",
    "1b.b.c.e"
  ],

我正在使用jq来解析此输出。

  1. 如何仅提取这两个ASG共有的字段?
  2. 如何仅提取这两个ASG独有的字段?

2 个答案:

答案 0 :(得分:2)

差/ 2

由于jq的“ - ”运算符是在数组上定义的,因此unique的一次调用就足以产生一个“无法识别”的答案:

def difference($a; $b): ($a | unique) - $b;

同样,对于对称差异,单个排序操作足以产生“未加”的值:

def sdiff($a; $b): (($a-$b) + ($b-$a)) | unique;

相交/ 2

以下是intersect/2的更快版本,可以与所有版本的jq一起使用 - 它取消了group_by,有利于sort

def intersect(x;y):
  ( (x|unique) + (y|unique) | sort) as $sorted
  | reduce range(1; $sorted|length) as $i
      ([];
       if $sorted[$i] == $sorted[$i-1] then . + [$sorted[$i]] else . end) ;

相交/ 2

如果你有jq 1.5,那么这里有一个相似但仍然明显更快的集合交叉函数:它在两个数组的集合交集中产生一个元素流:

def intersection(x;y):
  (x|unique) as $x | (y|unique) as $y
  | ($x|length) as $m
  | ($y|length) as $n
  | if $m == 0 or $n == 0 then empty
    else { i:-1, j:-1, ans:false }
    | while(  .i < $m and .j < $n;
        $x[.i+1] as $nextx
        | if $nextx == $y[.j+1] then {i:(.i+1), j:(.j+1), ans: true, value: $nextx}
          elif  $nextx < $y[.j+1] then .i += 1 | .ans = false
          else  .j += 1 | .ans = false
          end )
    end
  | if .ans then .value else empty end ;

答案 1 :(得分:1)

要查找两个数组之间的公共项,只需在两者之间执行一组交集。没有可用的交叉功能,但它应该足够简单,可以自己定义。获取每个数组的唯一项,按值对其进行分组,然后获取组中多于1个的项。

def intersect($a; $b): [($a | unique)[], ($b | unique)[]]
    | [group_by(.)[] | select(length > 1)[0]];

使用它来查找公共元素(假设您的输入实际上是一个有效的json对象):

$ jq 'def intersect($a; $b): [($a | unique)[], ($b | unique)[]]
    | [group_by(.)[] | select(length > 1)[0]];
intersect(.tag_aws_autoscaling_groupName_asg_test;
          .tag_aws_autoscaling_groupName_asg_unknown)' < input.json
[
  "aa.b.bb.55"
]

要查找数组特有的项,只需执行设置差异。

$ jq 'def difference($a; $b): ($a | unique) - ($b | unique);
difference(.tag_aws_autoscaling_groupName_asg_test;
           .tag_aws_autoscaling_groupName_asg_unknown)' < input.json
[
  "1b.b.c.d"
]