274

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.

doctororange
  • 11,670
  • 12
  • 42
  • 58
  • 6
    This question is quite helpful but I couldn't stop wondering why would one need an array of @fathers :P – ARK Aug 12 '20 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. – cesoid Apr 15 '23 at 18:27

4 Answers4

478

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."

Dave Powers
  • 2,051
  • 2
  • 30
  • 34
Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • 25
    Oh! You were the first one! Deleting my answer and +1. – Milan Novota Feb 11 '10 at 14:13
  • 30
    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. – Leigh McCulloch Jun 01 '14 at 07:35
  • 1
    Is it possible to return the index of where this was found in the original array of hashes? – Ian Warner Oct 18 '16 at 16:42
  • 1
    @IanWarner Yes. I suggest looking at the docs for the Enumerable module. If you still can't figure it out, post a new question. – Jordan Running Oct 18 '16 at 16:45
  • 1
    I just did this index = ARRAY.index { | h | h[ :code ] == ARRAY[ "code" ] } – Ian Warner Oct 18 '16 at 16:58
  • what is the fastest way to find it using the unique hash value, with no condition, the condition is `father["unique_id"] == 35` and not `>` and we use a unique id instead of age ? Any suggestions ? I wan to find out the the father with that id. – user1735921 Dec 19 '16 at 12:42
221

this will return first match

@fathers.detect {|f| f["age"] > 35 }
Naveed
  • 11,057
  • 2
  • 44
  • 63
  • 7
    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 `[]`. – TJ Biddle Sep 05 '13 at 18:42
  • 16
    You could also use `find` instead of `detect` for a more readable code – Alter Lagos Nov 13 '13 at 16:16
  • 8
    `find` can get confusing in rails, however. – user12341234 Jun 19 '15 at 05:21
  • 8
    `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` – Naveed Sep 05 '17 at 20:26
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

Hitesh Ranaut
  • 751
  • 6
  • 12
  • 1
    If the name was lowercase like `"hitesh"`, it wont return the hash. How can we account for word casing as well in such cases? – arjun Apr 20 '18 at 19:47
  • 2
    you can use something like. array.find {|x| x[:name].downcase == "Hitesh".downcase } – Hitesh Ranaut Apr 23 '18 at 15:25
  • @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"` – ARK Aug 12 '20 at 11:14
  • actually check my answer: https://stackoverflow.com/a/63375479/10313894 – ARK Aug 12 '20 at 11:24
  • `find` is an alias for `detect` method – netwire Jul 17 '21 at 10:30
6

(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 }

ARK
  • 772
  • 7
  • 21