开发者

Compare multiple values in a Ruby array

[
    {
        "name": "John Doe",
        "location": {
            "name": "New York, New York",
            "id": 12746342329
        },
        "hometown": {
            "name": "Brooklyn, New York",
            "id": 43453644
        }
   开发者_StackOverflow },
    {
        "name": "Jane Doe",
        "location": {
            "name": "Miami, Florida",
            "id": 12746342329
        },
        "hometown": {
            "name": "Queens, New York",
            "id": 12746329
        }
    }
]

Given this piece of JSON, how would I be able to loop through and pull out all of the "hometown" and "location" keys and see which people had the value of New York?

My issue is I can Array.each through these items, but I don't know how to traverse both location && hometown with my criteria ("New York").


people.select {|person|
  person.any? {|k, v|
    %w[location hometown].include?(k) && /New York/ =~ v['name']
}}

This basically says the following: select all entries in the array for which the following condition is true. The condition is: is it true for any of the key-value pairs that the key is either 'hometown' or 'location' and the name property of the value belonging to that key matches the Regexp /New York/?

However, your object model seems to be in a serious need of refactoring. In fact, the main problem is that your object model isn't even an object model, it's a hash and array model.

Here's what I mean by a proper object model:

class Person
  attr_reader :name, :location, :hometown

  def initialize(name, location=nil, hometown=nil)
    @name, @location, @hometown = name, location, hometown
  end

  def cities
    return @location, @hometown
  end
end

class City
  attr_reader :id, :name

  def initialize(id, name)
    @id, @name = id, name
  end

  def =~(other)
    name =~ other
  end
end

nyc = City.new(12746342329, 'New York, New York')
brooklyn = City.new(43453644, 'Brooklyn, New York')
miami = City.new(12746342329, 'Miami, Florida')
queens = City.new(12746329, 'Queens, New York')

john = Person.new('John Doe', nyc, brooklyn)
jane = Person.new('Jane Doe', miami, queens)

people = [john, jane]

If you have such a proper object model, your code becomes much cleaner, because instead of teasing apart the nuts of bults of a nested maze of hashes and arrays, you have nice little objects that you can simply ask some questions:

people.select {|person| person.cities.any? {|city| city =~ /New York/ }}

You can almost read this like English: from the array select all people for which any of their cities matches the Regexp /New York/.

If we improve the object model further, it gets even better:

class Person
  def lived_in?(pattern)
    cities.any? {|city| city =~ pattern }
  end
end

people.select {|person| person.lived_in?(/New York/) }

This basically says "From the people, select the ones which at one time lived in New York". That's much better than "from the people select all for which the first element of the key value pair is either the string 'hometown' or the string 'location' and the second element of the key value pair matches the Regexp /New York/".


I think Jörg's solution has a minor bug - 'location' and 'hometown' are not used, so, for example, following "person" would pass the test:

{
  'name' => 'Foo Bar',
  'favourite movie' => {
    name => 'New York, New York!'
  }
}

Here's a shot at correcting it, along with comments:

ary.select {|person| # get me every person satisfying following condition
  %w[location hometown].any? {|key| # check if for any of the strings 'location' and 'hometown'
    # person should have that key, and its 'name' should contain /New York/ regexp
    person[key] && person[key]['name'] && /New York/ =~ person[key]['name']
}}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜