Reputation: 9826
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
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
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