Ruby Question

# Ruby - array intersection (with duplicates)

I have

`array(1 and 2)`
. How can I get
`array3`
from them?

``````array1 = [2,2,2,2,3,3,4,5,6,7,8,9]

array2 = [2,2,2,3,4,4,4,4,8,8,0,0,0]

array3 = [2,2,2,3,4,8]
``````

`array1 & array2`
returns
`[2,3,4,8]`
, but I need to hold onto the duplicates.

``````(array1 & array2).flat_map { |n| [n]*[array1.count(n), array2.count(n)].min }
#=> [2,2,2,3,4,8]
``````

The steps:

``````a = array1 & array2
#=> [2, 3, 4, 8]
``````

The first element of `a` (`2`) is passed to the block and assigned to the block variable:

``````n = 2
``````

and the block calculation is performed:

``````[2]*[array1.count(2), array2.count(2)].min
#=> [2]*[4,3].min
#=> [2]*3
#=> [2,2,2]
``````

so `2` is mapped to `[2,2,2]`. The calculations are similar for the remaining three elements of `a`. As I am using `flat_map`, this returns `[2,2,2,3,4,8]`.

Do you have trouble remembering how Enumerable#flat_map differs from Enumerable#map? Suppose I had used `map` rather than `flat_map`. Then

``````a.map { |n| [n]*[array1.count(n), array2.count(n)].min }
#=> [[2, 2, 2], [3], [4], [8]]
``````

`flat_map` does nothing more that put a splat in front of each of those arrays:

``````[*[2, 2, 2], *[3], *[4], *[8]]
#=> [2, 2, 2, 3, 4, 8]
``````

If the arrays `array1` and `array2` are large and efficiency is a concern, we could do a bit of O(N) pre-processing:

``````def cnt(arr)
arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
end

cnt1 = cnt(array1)
#=> {2=>4, 3=>2, 4=>1, 5=>1, 6=>1, 7=>1, 8=>1, 9=>1}
cnt2 = cnt(array2)
#=> {2=>3, 3=>1, 4=>4, 8=>2, 0=>3}

(array1 & array2).flat_map { |n| [n]*[cnt1[n], cnt2[n]].min }
#=> [2,2,2,3,4,8]
``````
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download