how to get the intersection of two JSON arrays using jq

不羁的心 提交于 2019-12-11 00:08:06

问题


Given arrays X and Y (preferably both as inputs, but otherwise, with one as input and the other hardcoded), how can I use jq to output the array containing all elements common to both? e.g. what is a value of f such that

echo '[1,2,3,4]' | jq 'f([2,4,6,8,10])'

would output

[2,4]

?

I've tried the following:

map(select(in([2,4,6,8,10])))  --> outputs [1,2,3,4]
select(map(in([2,4,6,8,10])))  --> outputs [1,2,3,4,5]

回答1:


A simple and quite fast (but somewhat naive) filter that probably does essentially what you want can be defined as follows: # x and y are arrays def intersection(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) ;

If x is provided as input on STDIN, and y is provided in some other way (e.g. def y: ...), then you could use this as: intersection(.;y)

Other ways to provide two distinct arrays as input include:

 * using the --slurp option
 * using "--arg a v" (or "--argjson a v" if available in your jq)

Here's a simpler but slower def that's nevertheless quite fast in practice: def i(x;y): if (y|length) == 0 then [] else (x|unique) as $x | $x - ($x - y) end ;

Here's a standalone filter for finding the intersection of arbitrarily many arrays:

# Input: an array of arrays
def intersection:
  def i(y): ((unique + (y|unique)) | sort) as $sorted
  | reduce range(1; $sorted|length) as $i
       ([]; if $sorted[$i] == $sorted[$i-1] then . + [$sorted[$i]] else . end) ;
  reduce .[1:][] as $a (.[0]; i($a)) ;

Examples:

[ [1,2,4], [2,4,5], [4,5,6]] #=> [4]
[[]]                         #=> []
[]                           #=> null

Of course if x and y are already known to be sorted and/or unique, more efficient solutions are possible. See in particular https://rosettacode.org/wiki/Set#Finite_Sets_of_JSON_Entities




回答2:


$ echo '[1,2,3,4] [2,4,6,8,10]' | jq --slurp '[.[0][] as $x | .[1][] | select($x == .)]'
[
  2,
  4
]



回答3:


Here is a solution which works by counting occurrences of elements in the arrays using foreach

[
  foreach ($X[], $Y[]) as $r (
    {}
  ; .[$r|tostring] += 1
  ; if .[$r|tostring] == 2 then $r else empty end
  )
]

If this filter is in filter.jq then

jq -M -n -c --argjson X '[1,2,3,4]' --argjson Y '[2,4,6,8,10]' -f filter.jq

will produce

[2,4]

It assumes there are no duplicates in the initial arrays. If that's not the case then it is easy to compensate with unique. E.g.

[
  foreach (($X|unique)[], ($Y|unique)[]) as $r (
    {}
  ; .[$r|tostring] += 1
  ; if .[$r|tostring] == 2 then $r else empty end
  )
]


来源:https://stackoverflow.com/questions/38364458/how-to-get-the-intersection-of-two-json-arrays-using-jq

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!