squarism
squarism

Reputation: 3307

Filtering Ruby array of hashes by another hash

Perhaps I'm missing something obvious. It seems tricky to filter a hash by another hash or multiple key/value pairs.

fruit = [
  { name: "apple",   color: "red",    pieable: true  },
  { name: "orange",  color: "orange", pieable: false },
  { name: "grape",   color: "purple", pieable: false },
  { name: "cherry",  color: "red",    pieable: true  },
  { name: "banana",  color: "yellow", pieable: true  },
  { name: "tomato",  color: "red",    pieable: false }
]
filter = { color: "red", pieable: true }

# some awesome one-liner? would return
[
  { name: "apple",   color: "red",    pieable: true  },
  { name: "cherry",  color: "red",    pieable: true  }
]      

The array of hashes I don't think is the problem. I don't even know how to test a hash by another arbitrary hash. I'm using Rails so anything out of active_support etc is fine.

Upvotes: 3

Views: 1077

Answers (6)

Rahul Patel
Rahul Patel

Reputation: 1424

Try this

fruit.select{ |hash| (filter.to_a & hash.to_a) == filter.to_a }

=> [{:name=>"apple", :color=>"red", :pieable=>true}, 
    {:name=>"cherry", :color=>"red", :pieable=>true}] 

Upvotes: 0

squarism
squarism

Reputation: 3307

Tony Arcieri (@bascule) gave this really nice solution on twitter.

require 'active_support/core_ext'  # unneeded if you are in a rails app
fruit.select { |hash| hash.slice(*filter.keys) == filter }

And it works.

# [{:name=>"apple", :color=>"red", :pieable=>true},
# {:name=>"cherry", :color=>"red", :pieable=>true}]

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110675

I'd be inclined to use Enumerable#group_by for this:

fruit.group_by { |g| { color: g[:color], pieable: g[:pieable] } }[filter]
  #=> [{:name=>"apple",  :color=>"red", :pieable=>true},
  #    {:name=>"cherry", :color=>"red", :pieable=>true}]

Upvotes: 1

SHS
SHS

Reputation: 7744

COULD be made into a one liner. But multi-line is cleaner.

fruit.select do |hash| # or use select!
  filter.all? do |key, value|
    value == hash[key]
  end
end

Upvotes: 2

Chris Heald
Chris Heald

Reputation: 62648

Not the most efficient (you could just use an array form of filter to avoid repeated conversions), but:

fruit.select {|f| (filter.to_a - f.to_a).empty? }

Upvotes: 1

Casper
Casper

Reputation: 34308

If you allow two lines it could also be made into an efficient "one-liner" like so:

keys, values = filter.to_a.transpose 
fruit.select { |f| f.values_at(*keys) == values }

Upvotes: 1

Related Questions