I have two arrays. One that stores a strings name
and one that stores hashes people
. I'd like to filter the people
array and find the one hash that either has a close/full match with one of the strings in my name array. So for instance John from names
and John Doe would be consider a match because the both have John in them.
names = ["John", "Jane"]
people = [{name: "John Doe"}, {name: "Bill Bradley"}]
I tried matching the two using the select
method but it returned an empty array?
people.select{|person| person["name"] == names.any?}
Try this:
matches = people.select do |person|
!(names & person[:name].split(' ')).empty?
end
p matches #=> [{:name=>"John Doe"}]
Basically, you're first separating the hash value into individual words with property[:name].split(' ')
. The result of this will be array like ['John', 'Doe']
. You then check if this newly created array has any common elements with the names array using the & operator. Thus, you get names & property[:name].split(' ')
. If no matches are produced, you'll have an empty array which is NOT what you want, thus the !
(not) operator. You could also do this if it's more intuitive:
matches = people.reject do |person|
(names & person[:name].split(' ')).empty?
end
Also, if you'd like to get the hash itself rather than the hash wrapped in an array (or hashes, if there are multiple results), replace select
with detect
.
This would be one way:
names = ["John", "Jane"]
people = [{name: "Johnny Jones"}, {name: "John Doe"}, {name: "Bill Bradley"}]
people.select { |h| hname = h[:name]; names.any? { |n| hname[/\b#{n}\b/i] } }
#=> [{:name=>"John Doe"}]
The steps:
enum = people.select
#=> #<Enumerator: [{:name=>"Johnny Jones"}, {:name=>"John Doe"},
# {:name=>"Bill Bradley"}]:select>
The first element of the enumerator is passed into the block and assigned to the block variable h
:
h = enum.next
#=> {:name=>"Johnny Jones"}
Next I create a temporary variable so we don't have to retieve h[:name]
for each element of enum
passed into the block:
hname = h[:name]
#=> "Johnny Jones"
Then see if any element of names
matches "Johnny Jones"
:
names.any? { |n| hname[/\b#{n}\b/i] }
#=> ["John", "Jane"].any? { |n| "Johnny Jones"[/\b#{n}\b/i] }
#=> false
In the last expression, "John"
is passed into the block and assigned to the block variable n
. We then have:
"Johnny Jones"[/\bJohn\b/i]
#=> nil
The first word break ( \\b
) requires that "John"
be immediately preceded by a non-word character (or be the first character of the string); the second word break requires that "John"
be immediately followed by a non-word character (or be the last character in the string). Therefore, "John"
does not match "Johnny Jones"
.
The next (and last) element of names
( "Jane"
is then passed into the block and assigned to n
. "Johnny Jones"
doesn't match "Jane"
either, so any?
returns false
and poor Johnny doesn't get selected.
In a similar manner, John Doe is selected and Bill Bradley is sent home.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.