Blubber
Blubber

Reputation: 1473

Finding what is the same with two arrays with duplicates

I want to find all elements that are the same between two arrays, including elements that are repeated multiple times in both arrays. For instance, I want

[1,1,1,2]

and

[1,1,3,4]

to give

[1,1]

I could iterate through the array and keep track of it, but I was wondering if there were a more ruby way of doing this.

Upvotes: 0

Views: 77

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110675

a = [1,1,1,2,5]
b = [1,1,3,2,4,2]

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

hb = counting_hash(b)
  #=> {1=>2, 3=>1, 2=>2, 4=>1}
counting_hash(a).each_with_object([]) do |(k,v),a|
  n = [v, hb[k]].min
  n.times { a << k } if n > 0
end
  #=> [1, 1, 2]

Note that

counting_hash(a)
  #=> {1=>3, 2=>1, 5=>1}

and

hb[5]
  #=> 0

as hb has a default value of zero.

Upvotes: 1

J&#246;rg W Mittag
J&#246;rg W Mittag

Reputation: 369458

It looks like you want multiset-intersection. There is no multiset datastructure in the Ruby core lib or stdlib, but there are multiple implementations you can find on the web. I'll just randomly pick one (it doesn't really matter, multiset is a mathematical concept with strict definition, all implementations behave more or less the same):

require 'multiset'

ms = Multiset[1, 1, 1, 2] & Multiset[1, 1, 3, 4]
#=> #<Multiset:#2 1>

ms.to_a
#=> [1, 1]

If you must, you can convert it to an array using Multiset#to_a, but remember: most of the interesting methods you can call on Array are actually from the Enumerable mixin and thus available to all collections; you often don't actually need to have an array.

Upvotes: 5

Related Questions