Find duplicate objects in an array based on a subset of attributes in Ruby

907 Views Asked by At

I have an array of car objects,

class Car

  def initialize(engine_size, maximum_speed, acceleration, mass)
    @engine_size = engine_size
    @maximum_speed = maximum_speed
    @acceleration = acceleration
    @mass = mass
  end

end

and I would like to list identical cars by comparing a subset of their attributes, for example: engine_size, acceleration and mass.

I have tried using group_by however this only allows me to compare the entire car object or single attributes.

4

There are 4 best solutions below

1
On BEST ANSWER
cars.group_by{|car| [car.engine_size, car.accelaration, car.mass]}
0
On

Let's assume you have previusly initialized an array of cars and now you want a subset of cars that have a 2000cc engine and more than 1000kg of mass:

result = cars.select{|car| car.engine==2000 and car.mass > 1000}

Also you can use select! if you need to filter in place. More about on docs.

0
On

If you look up the docs on group_by you'll find that you can give it a block. If you make the block return the subset of attributes you're interested in, that should give you the required result.

0
On

This allows you to filter cars by different search attributes :

class Car

  attr_reader :engine_size, :maximum_speed, :acceleration, :mass

  def self.cars_by_attrs(cars, args = {})

    return [] if cars.empty?

    # Checking if all the arguments in args are valid
    # instance variables on all the cars.
    all_instance_vars = cars.all? do |car|
      args.all? do |key, val|
        car.instance_variables.include? "@#{key.to_s}".to_sym
      end
    end

    unless all_instance_vars
      raise ArgumentError.new('Instance variable not found.')
    end

    cars.select do |car|
      args.all? do |key, val|
        # Checking if the instance variables can be retrieved.
        unless car.respond_to?(key)
          raise ArgumentError.new('Instance variable not accessible.') 
        end
        car.send(key) == val
      end
    end
  end

  def initialize(engine_size, maximum_speed, acceleration, mass)
    @engine_size = engine_size
    @maximum_speed = maximum_speed
    @acceleration = acceleration
    @mass = mass
    @test = true
  end


end

cars = []
cars << Car.new(10, 20, 5, 6)
cars << Car.new(10, 20, 7, 8)
cars << Car.new(12, 21, 9, 10)
puts Car.cars_by_attrs(cars, engine_size: 10, maximum_speed: 20)
# First two cars.
puts Car.cars_by_attrs(cars, mass: 10)
# 3rd car.
# puts Car.cars_by_attrs(cars, new: 10)
# Error !
# puts Car.cars_by_attrs(cars, test: 10)
# Error !