Home > other >  Select unique objects based on an attribute
Select unique objects based on an attribute

Time:04-01

I have an array of objects, with each object having an origin and a destination. And each origin or destination has a name and a point. I want to return the unique origin-destination pairs based on the name. For example, in the array below:

[
  obj1,
  obj2,
  obj3,
  obj4
]

where

obj1 = (o1=(name: "name 1", point: "x1, y1"), d1=(name: "name 3", point: "x2, y2"))
obj2 = (o2=(name: "name 2", point: "x3, y3"), d2=(name: "name 4", point: "x4, y4"))
obj3 = (o3=(name: "name 1", point: "x5, y5"), d3=(name: "name 3", point: "x6, y6"))
obj4 = (o4=(name: "name 2", point: "x7, y7"), d4=(name: "name 4", point: "x8, y8"))

obj1 is considered to be the same as obj3, since obj1.o1.name = obj3.o3.name and obj1.d1.name = obj3.d3.name. Similarly, obj2 and obj4 are the same. How can I return only obj1 (or obj2) and obj3 (or obj4) from the array above?

The code below looks at the entire origin or destination for uniqueness consideration, but I need to only consider the name of the origin and destinations.

my_array.map { |obj| [obj.origin, obj.destination] }
        .uniq

CodePudding user response:

I think it's best to reverse the operation by getting the unique objects first then mapping to get the point.

Array#uniq takes a block

arr
 .uniq { |obj| obj.name }
 .map { |obj| [obj.origin, obj.destination] }

You can also use a shorthand for uniq .uniq(&:name)

CodePudding user response:

You can use uniq to get the uniq objects. You can also pass a block to uniq to get distinct elements based on your custom filters.

Documentation link - Array#uniq

So something like this -

# Objects initialization
obj1 = { origin: { name: "name 1", point: "x1, y1" }, destination: { name: "name 3", point: "x2, y2"} }
obj2 = { origin: { name: "name 2", point: "x3, y3" }, destination: { name: "name 4", point: "x4, y4" }}
obj3 = { origin: { name: "name 1", point: "x5, y5" }, destination: { name: "name 3", point: "x6, y6"} }
obj4 = { origin: { name: "name 2", point: "x7, y7" }, destination: { name: "name 4", point: "x8, y8" }}

my_array = [obj1, obj2, obj3, obj4]

# Now to get distinct by just origin name
my_array.uniq { |obj| obj[:origin][:name] }
#=> [obj1, obj2]

# To get distinct by origin name and destination name
# You can create an array of both names in uniq block
my_array.uniq { |obj| [obj[:origin][:name], obj[:destination][:name]] }
# => [obj1, obj2]

CodePudding user response:

uniq could take a block

my_array.uniq { |obj| [obj.origin.name, obj.destination.name]}
        .map { |obj| [obj.origin, obj.destination] }

# or

my_array.map { |obj| [obj.origin, obj.destination] }
        .uniq { |pair| pair.map(&:name) } 
  • Related