How to Get the Intersection, Union, and Subset of Arrays in Ruby

How can I get the intersection, union, and subset of arrays in Ruby?

Utilizing the fact that you can do set operations on arrays by doing &(intersection), -(difference), and |(union).

Obviously I didn't implement the MultiSet to spec, but this should get you started:

class MultiSet
attr_accessor :set
def initialize(set)
@set = set
end
# intersection
def &(other)
@set & other.set
end
# difference
def -(other)
@set - other.set
end
# union
def |(other)
@set | other.set
end
end

x = MultiSet.new([1,1,2,2,3,4,5,6])
y = MultiSet.new([1,3,5,6])

p x - y # [2,2,4]
p x & y # [1,3,5,6]
p x | y # [1,2,3,4,5,6]

Intersection of two arrays coming up empty

Your @tasks looks like an array of arrays. Try to flatten it before intersection.

Performing union of two arrays with custom rules

b = ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]
a = ["Jon Roberts", "Wil Koleva", "Lilian Joe", "Vic Jane", "Al Thomas"]

r = /
\s # match a space
[[:alpha:]]+ # match > 0 alphabetic characters
\z # match end of string
/x # free-spacing regex definition mode

(b+a).uniq { |str| [str[0], str[r]] }
#=> ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]

This uses the form of the method Array#uniq that employs a block.

You may alternatively write (b|a).uniq { |str| [str[0], str[r]] }

The steps are as follows.

c = b+a
# => ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas",
# "Jon Roberts", "Wil Koleva", "Lilian Joe", "Vic Jane", "Al Thomas"]

The first element of c passed to the block is

str = c.first
#=> "John Roberts"

so the block calculation is

[str[0], str[r]]
#=> ["J", " Roberts"]

The calculations are similar for all the other elements of c. The upshot is that

c.uniq { |str| [str[0], str[r]] }

is equivalent to selecting the first elements of c, when converted to [<first name initial>, <last name>], that match an element of the array d, where

d = [["J", "Roberts"], ["W", "Koleva"], ["L", "Joe"], ["V", "Jane"], ["A", "Thomas"],
["J", "Roberts"], ["W", "Koleva"], ["L", "Joe"], ["V", "Jane"], ["A", "Thomas"]].uniq
#=> [["J", "Roberts"], ["W", "Koleva"], ["L", "Joe"], ["V", "Jane"], ["A", "Thomas"]]

Pascal suggested that it would be better for uniq's block to return a string:

{ |str| "#{str[0]} #{str[r]}" }

(e.g., "J Roberts") which might instead be written

{ |str| str.sub(/(?<=.)\S+/,"") }

The inclusion of the space after the first initial is optional (e.g., "JRoberts" would also work).

Merging inner arrays of indices if they contain the same content

x.sort.inject([]) do |y, new|
(((y.last || []) & new).length > 0) ? y[0..-2].push(y.last | new) : y.push(new)
end.map(&:sort)

How does this code find the rectangle intersection?

Sometimes it helps to see code written a little differently:

def rec_intersection(rect1, rect2)

x_min = [rect1.top_left.x, rect2.top_left.x].max # => 1
y_min = [rect1.top_left.y, rect2.top_left.y].max # => 1

x_max = [rect1.bottom_right.x, rect2.bottom_right.x].min # => 2
y_max = [rect1.bottom_right.y, rect2.bottom_right.y].min # => 2

Rectangle.new(
Point.new(x_min, y_min),
Point.new(x_max, y_max)
)

end

Point = Struct.new(:x, :y)
Rectangle = Struct.new(:top_left, :bottom_right)

rect1 = Rectangle.new(Point.new(1, 1), Point.new(2, 2))
# => #<struct Rectangle
# top_left=#<struct Point x=1, y=1>,
# bottom_right=#<struct Point x=2, y=2>>

rect2 = Rectangle.new(Point.new(0, 0), Point.new(5, 5))
# => #<struct Rectangle
# top_left=#<struct Point x=0, y=0>,
# bottom_right=#<struct Point x=5, y=5>>

rec_intersection(rect1, rect2)
# => #<struct Rectangle
# top_left=#<struct Point x=1, y=1>,
# bottom_right=#<struct Point x=2, y=2>>


Related Topics



Leave a reply



Submit