iamse7en
iamse7en

Reputation: 609

Ruby: Extract from deeply nested JSON structure based on multiple criteria

I want to select any marketId of marketName == 'Moneyline' but only those with countryCode == 'US' || 'GB' OR eventName.include?(' @ '). (space before and after the @). I tried different combos of map and select but some nodes don't have countryCode which complicates things for me. This is the source, but a sample of what it might look like:

{"currencyCode"=>"GBP",
"eventTypes"=>[
    {"eventTypeId"=>7522,
    "eventNodes"=>[
        {"eventId"=>28024331,
        "event"=>
            {"eventName"=>"EWE Baskets Oldenburg v PAOK Thessaloniki BC"
            },
            "marketNodes"=>[
                {"marketId"=>"1.128376755",
                "description"=>
                    {"marketName"=>"Moneyline"}
                },
                {"marketId"=>"1.128377853",
                "description"=>
                    {"marketName"=>"Start Lublin +7.5"}
                }}}]},
        {"eventId"=>28023434,
        "event"=>
            {"eventName"=>"Asseco Gdynia v Start Lublin",
            "countryCode"=>"PL",
            },
            "marketNodes"=>
                [{"marketId"=>"1.128377853", ETC...

Upvotes: 1

Views: 381

Answers (1)

Eric Duminil
Eric Duminil

Reputation: 54263

Based on this previous answer, you just need to add a select on eventNodes :

require 'json'

json = File.read('data.json')
hash = JSON.parse(json)

moneyline_market_ids = hash["eventTypes"].map{|type|
  type["eventNodes"].select{|event_node|
    ['US', 'GB'].include?(event_node["event"]["countryCode"]) || event_node["event"]["eventName"].include?(' @ ')
  }.map{|event|
    event["marketNodes"].select{|market|
      market["description"]["marketName"] == 'Moneyline'
    }.map{|market|
      market["marketId"]
    }
  }
}.flatten

puts moneyline_market_ids.join(', ')
#=> 1.128255531, 1.128272164, 1.128255516, 1.128272159, 1.128278718, 1.128272176, 1.128272174, 1.128272169, 1.128272148, 1.128272146, 1.128255464, 1.128255448, 1.128272157, 1.128272155, 1.128255499, 1.128272153, 1.128255484, 1.128272150, 1.128255748, 1.128272185, 1.128278720, 1.128272183, 1.128272178, 1.128255729, 1.128360712, 1.128255371, 1.128255433, 1.128255418, 1.128255403, 1.128255387

If you want to keep the country code and name information with the id:

moneyline_market_ids = hash["eventTypes"].map{|type|
  type["eventNodes"].map{|event_node|
    [event_node, event_node["event"]["countryCode"], event_node["event"]["eventName"]]
  }.select{|_, country, event_name|
    ['US', 'GB'].include?(country) || event_name.include?(' @ ')
  }.map{|event, country, event_name|
    event["marketNodes"].select{|market|
      market["description"]["marketName"] == 'Moneyline'
    }.map{|market|
      [market["marketId"],country,event_name]
    }
  }
}.flatten(2)

require 'pp'
pp moneyline_market_ids
#=> [["1.128255531", "US", "Philadelphia @ Seattle"],
#   ["1.128272164", "US", "Arkansas @ Mississippi State"],
#   ["1.128255516", "US", "New England @ San Francisco"],
#   ["1.128272159", "US", "Indiana @ Michigan"],
#   ["1.128278718", "CA", "Edmonton @ Ottawa"],
#   ["1.128272176", "US", "Arizona State @ Washington"],
#   ["1.128272174", "US", "Alabama A&M @ Auburn"],
#    ...

Upvotes: 2

Related Questions