Filtering Ruby array of hashes by another hash

Perhaps I am missing something obvious. It seems difficult to filter the hash with 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  }
]      

      

An array of hashes that I don't think of is the problem. I don't even know how to check the hash with another arbitrary hash. I am using Rails, so nothing from active_support etc. OK.

+3


source to share


6 answers


CAN be made in one liner. But multi-line is cleaner.



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

      

+2


source


If you allow two lines, this can also be turned into an effective "one line":



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

      

+1


source


Not the most efficient (you can just use the array shape filter

to avoid repeated conversions), but:

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

      

+1


source


I would tend 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}]

      

+1


source


Tony Arieri (@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}]

      

0


source


try it

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

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

      

0


source







All Articles