B Hillsworth
B Hillsworth

Reputation: 3

Looping through an array and adding that data with a new key to hashes in a separate array

I am currently working on an app that scrapes data from mlb.com and the data is saved in arrays. I then want to iterate over the array of data and add each element to a hash. Here is a vanilla example of what I am attempting to do...

players = []

names = ["Kyle Freeland", "Jon Gray", "DJ Johnson"]

names.each do |name|
  players << {:name => name}
end

players 
=> [{:name=>"Kyle Freeland"}, {:name=>"Jon Gray"}, {:name=>"DJ 
Johnson"}]

So far this is exactly what I want but I also have a separate array storing each players' number that I am trying to push into each hash with a key of "number"...

numbers = ["21", "55", "63"]

I've been trying to do this by...

numbers.each do |number|
  players.each do |player|
    player[:number] = number
  end
end

Which ultimately gives me...

players
[{:name=>"Kyle Freeland", :number=>"63"}, {:name=>"Jon Gray", 
:number=>"63"}, {:name=>"DJ Johnson", :number=>"63"}]

So the final number is being pushed into each hash. Is there an easier way anybody can think of to push the correct numbers where they need to be? All data I am scraping will be in order.

Upvotes: 0

Views: 72

Answers (5)

Eneroth3
Eneroth3

Reputation: 1297

Is this what you are looking for?

names = ["Kyle Freeland", "Jon Gray", "DJ Johnson"]
numbers = ["21", "55", "63"]

names.zip(numbers).map { |name, number| {name: name, number: number } }

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110675

Your design has some weaknesses. You have two arrays containing information on players, with the ith element of each array pertaining to the same player. Presumably, if you wanted to add additional information for each player, such as batting averages, you would create an array of the same length as the first two that contained batting averages, being careful to keep them in the correct order. (I say "his" because at the present time all MLB players are men.)

That model is prone to error. Also, what will you do when you want to add data for additional players?

The first thing you need is a unique identifier for each player. For simplicity, let's assume that's his name, recognising that in a real application we would need to address the possibility of two players having the same name.1 Whenever you collect each datum it needs to be associated with the player's identifying characteristic, which we are assuming is his name, so we don't have to worry about the order of elements in arrays. For example, let's make our database an initially-empty hash, players, the keys being the players names and the values being a hash containing information for that player.

players = {}

Now suppose we want to add three players and their numbers to the hash:

numbers = {
  "Kyle Freeland"=>{ "number"=>"21" }, 
  "Jon Gray"     =>{ "number"=>"55" },
  "DJ Johnson"   =>{ "number"=>"63" }
}

We can then do the following:

def add_data(players, data)
  data.each do |player, datum|
    players[player] = {} unless players.key?(player)
    players[player].update(data[player])
  end
end

add_data(players, numbers)
players
  #=> {"Kyle Freeland"=>{"number"=>"21"},
  #    "Jon Gray"=>{"number"=>"55"},
  #    "DJ Johnson"=>{"number"=>"63"}}

The first thing that add_data does is to see if the hash players has a key player (e.g, "Kyle Freeland"). If it does not it adds that key to players and sets it value to an empty hash. Next it merges the value of player, datum, with the value of player in players. See the doc Hash#update, which is the same as Hash#merge!.

Now suppose we wanted to add data for a new player, add batting averages for these three players and correct Jon Gray's number, which should be "56":

batting_average = {
  "Kyle Freeland"=>{ "avg"=>302 }, 
  "Jon Gray"     =>{ "avg"=>246 },
  "DJ Johnson"   =>{ "avg"=>280 }
}

new_player = {
  "Dusty Rhodes" =>{"number"=>"12", "avg"=>312 }
}

correction = {
  "Jon Gray"=>{ "number"=>"56" }
}

add_data(players, batting_average)
add_data(players, new_player)
add_data(players, correction)

players
  #=> {"Kyle Freeland"=>{"number"=>"21", "avg"=>302},
  #    "Jon Gray"     =>{"number"=>"56", "avg"=>246},
  #    "DJ Johnson"   =>{"number"=>"63", "avg"=>280},
  #    "Dusty Rhodes" =>{"number"=>"12", "avg"=>312}} 

I don't mean to suggest that this is what you should do, merely that it is one of many designs you could employ that make the data analysis more reliable and easier to use than your present approach. In fact, if this were the real world you would undoubtedly want to use an SQL database for this application.

Incidentally, all your fields are strings. That's of course not necessary. I've entered batting averages as integers, which would facilitate calculations such as determining a players average batting average over, say, five years. (Perhaps floats, such as 0.302, would be even better). Also, try using symbols for keys (e.g., :avg=>312, which you can alternatively write avg: 312). As well as saving keystrokes there are other advantages which I won't get into now. In general, use symbols for keys as much as possible.

1. In 1934, for example, the Cubs had a second baseman named Zaphod Beeblebrox and the Yankees had an outfielder with the same name.

Upvotes: 1

Raji
Raji

Reputation: 165

I hope this will help you.

names = ["Kyle Freeland", "Jon Gray", "DJ Johnson"]
numbers = ["21", "55", "63"]
names.zip(numbers).to_h

Upvotes: 0

kiddorails
kiddorails

Reputation: 13014

names = ["Kyle Freeland", "Jon Gray", "DJ Johnson"]
numbers = ["21", "55", "63"]

players = names.zip(numbers).map { |x| { name: x[0], number: x[1] } }
#=> [{:name=>"Kyle Freeland", :number=>"21"}, {:name=>"Jon Gray", :number=>"55"}, {:name=>"DJ Johnson", :number=>"63"}]

names.zip(numbers) basically merges elements of self with corresponding elements from each argument resulting in [["Kyle Freeland", "21"], ["Jon Gray", "55"], ["DJ Johnson", "63"]].

Then we are iterating on this array and making relevant hashes.

Upvotes: 2

Shimu
Shimu

Reputation: 1147

Here is a short example on how to solve this problem:

players = []

names = ["Kyle Freeland", "Jon Gray", "DJ Johnson"]
numbers = ["21", "55", "63"]

names.each_with_index do |name, idx|
  players << {:name => name, number: numbers[idx]}
end

Upvotes: 0

Related Questions