tags:

views:

74

answers:

2
[
    {
        "name": "John Doe",
        "location": {
            "name": "New York, New York",
            "id": 12746342329
        },
        "hometown": {
            "name": "Brooklyn, New York",
            "id": 43453644
        }
    },
    {
        "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").

+4  A: 
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/".

Jörg W Mittag
I've updated the JSON to be valid, do you mind giving it another look?
kylemac
@kylemac: it works.
Jörg W Mittag
@jorg - do you mind explaining what is happening here? I am new to ruby and this is a little over my head
kylemac
@jorg has provided a very interesting tutorial on how to build a database for your data using Ruby. (My thanks to you, Jorg.) However, I'm not sure if that's what you want to do. If it is, I think it may be too much to ask what all of Jorg's code is doing--better to first gain a better understanding of the language generally. Another possibility is that you want to parse a JSON file with Ruby and then use Ruby to answer your question about the data. Please clarify.
Cary Swoveland
+1  A: 

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']
}}
Mladen Jablanović
+1, Thanks for noticing that. Major brainfart on my part. I *did* actually remember to add a person from Chicago to the testset, so that I could determine whether the filter was working *at all* but I completely missed *that* particular corner case. Nice work! (Oh, and Yay for IDEs! If I had typed that code into my personal favorite, NetBeans, instead of the stupid StackOverflow WYSIWIG editor and IRB, it would have immediately highlighted the two as "unused local variables".)
Jörg W Mittag