282

I have an array of hashes, @fathers.

a_father = { "father" => "Bob", "age" =>  40 }
@fathers << a_father
a_father = { "father" => "David", "age" =>  32 }
@fathers << a_father
a_father = { "father" => "Batman", "age" =>  50 }
@fathers << a_father 

How can I search this array and return an array of hashes for which a block returns true?

For example:

@fathers.some_method("age" > 35) #=> array containing the hashes of bob and batman

Thanks.

2
  • 7
    This question is quite helpful but I couldn't stop wondering why would one need an array of @fathers :P Commented Aug 12, 2020 at 11:07
  • @ARK I was slightly preoccupied with that question, but that was completely eclipsed by my realization that each item in the @fathers hash has a 'father' key rather than a 'name' key. I wish I could believe that those are the fathers' fathers, but I can't. Commented Apr 15, 2023 at 18:27

5 Answers 5

490

You're looking for Enumerable#select (also called find_all):

@fathers.select {|father| father["age"] > 35 }
# => [ { "age" => 40, "father" => "Bob" },
#      { "age" => 50, "father" => "Batman" } ]

Per the documentation, it "returns an array containing all elements of [the enumerable, in this case @fathers] for which block is not false."

Sign up to request clarification or add additional context in comments.

6 Comments

Oh! You were the first one! Deleting my answer and +1.
As a note, if you wanted only to find a single one (the first one) you can use @fathers.find {|father| father["age"] > 35 } instead.
Is it possible to return the index of where this was found in the original array of hashes?
@IanWarner Yes. I suggest looking at the docs for the Enumerable module. If you still can't figure it out, post a new question.
I just did this index = ARRAY.index { | h | h[ :code ] == ARRAY[ "code" ] }
|
224

this will return first match

@fathers.detect {|f| f["age"] > 35 }

4 Comments

I prefer this over #select - But all goes for your use case. #detect will return nil if no match is found, while #select, in @Jordan's answer, will return [].
You could also use find instead of detect for a more readable code
find can get confusing in rails, however.
select and detect aren't same, select will transverse the whole array, while detect will stop as soon as the first match is found. IF you're looking for ONE match @fathers.select {|f| f["age"] > 35 }.first vs @fathers.detect {|f| f["age"] > 35 } for performance and readability, my vote goes for detect
45

if your array looks like

array = [
 {:name => "Hitesh" , :age => 27 , :place => "xyz"} ,
 {:name => "John" , :age => 26 , :place => "xtz"} ,
 {:name => "Anil" , :age => 26 , :place => "xsz"} 
]

And you Want To know if some value is already present in your array. Use Find Method

array.find {|x| x[:name] == "Hitesh"}

This will return object if Hitesh is present in name otherwise return nil

5 Comments

If the name was lowercase like "hitesh", it wont return the hash. How can we account for word casing as well in such cases?
you can use something like. array.find {|x| x[:name].downcase == "Hitesh".downcase }
@arjun array.any?{ |element| element[:name].casecmp("hitesh")==0 } should work for any case in start or anywhere in the string i.e. for "Hitesh", "hitesh" or "hiTeSh"
actually check my answer: stackoverflow.com/a/63375479/10313894
find is an alias for detect method
7

(Adding to previous answers (hope that helps someone):)

Age is simpler but in case of string and with ignoring case:

  • Just to verify the presence:

@fathers.any? { |father| father[:name].casecmp("john") == 0 } should work for any case in start or anywhere in the string i.e. for "John", "john" or "JoHn" and so on.

  • To find first instance/index:

@fathers.find { |father| father[:name].casecmp("john") == 0 }

  • To select all such indices:

@fathers.select { |father| father[:name].casecmp("john") == 0 }

Comments

0

The closest to @fathers.some_method("age" > 35) would be

@fathers.hselect("age", :>, 35)

← To enable this, one should evaluate the following

class TrueClass
  def to_boolean
    self
  end
end

class FalseClass
  def to_boolean
    self
  end
end

class Array
  def to_boolean
    !self.empty?
  end

  def select_criteria(field, op, val=nil)
    -> ha{
      if val.nil?
        ha[field].send(op)
      else
        ha[field].send(op, val)
      end.to_boolean}
  end

  def hop(method, field, op, val=nil)
    self.send(method, &select_criteria(field, op, val))
  end

  def hselect(field, op, val=nil)
    self.hop(:select, field, op, val)
  end

  def hreject(field, op, val=nil)
    self.hop(:reject, field, op, val)
  end
end

← where hreject is an example of another similar definition. More tricks are possible, for example:

@fathers << { "father" => "Sam", "age" =>  44, "kids_ages" => [3, 8, 14] }
@fathers.hreject("kids_ages", :nil?)
@fathers.hselect("kids_ages", :&, [8])

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.