Amin Shah Gilani
Amin Shah Gilani

Reputation: 9826

Intersections and Unions in Ruby for sets with repeated elements

How do we get intersections and unions in Ruby for sets that repeat elements.

# given the sets
a = ["A", "B", "B", "C", "D", "D"]
b = ["B", "C", "D", "D", "D", "E"]

# A union function that adds repetitions
union(a, b)
=> ["A", "B", "B", "C", "D", "D", "D", "E"]

# An intersection function that adds repetitions
intersection(a, b)
=> ["B", "C", "D", "D"]

The &, and | operators seem to ignore repetitions and duplicates, as written in the documentation.

# union without duplicates
a | b
=> ["A", "B", "C", "D", "E"]

# intersections without duplicates
a & b
=> ["B", "C", "D"]

Upvotes: 4

Views: 930

Answers (2)

Stefan
Stefan

Reputation: 114188

Building upon Cary Swoveland's answer, you could create a temporary hash to count the number of occurrences of each member in each array: (I've generalized the number of arguments)

def multiplicities(*arrays)
  m = Hash.new { |h, k| h[k] = Array.new(arrays.size, 0) }
  arrays.each_with_index { |ary, idx| ary.each { |x| m[x][idx] += 1 } }
  m
end

multiplicities(a, b)
#=> {"A"=>[1, 0], "B"=>[2, 1], "C"=>[1, 1], "D"=>[2, 3], "E"=>[0, 1]}

Implementing union and intersection is straight forward:

def union(*arrays)
  multiplicities(*arrays).flat_map { |x, m| Array.new(m.max, x) }
end

def intersection(*arrays)
  multiplicities(*arrays).flat_map { |x, m| Array.new(m.min, x) }
end

union(a, b)        #=> ["A", "B", "B", "C", "D", "D", "D", "E"]
intersection(a, b) #=> ["B", "C", "D", "D"]

With this approach each array has to be traversed only once.

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110685

def union(a,b)
  (a|b).flat_map { |s| [s]*[a.count(s), b.count(s)].max }
end

union(a,b)
  # => ["A", "B", "B", "C", "D", "D", "D", "E"] 

def intersection(a,b)
  (a|b).flat_map { |s| [s]*[a.count(s), b.count(s)].min }
end

intersection(a,b)
  #=> ["B", "C", "D", "D"]

Upvotes: 3

Related Questions