3

I have two arrays of hashes. The keys for the hashes are different:

player_scores1 = [{:first_name=>"Bruce", :score => 43, :time => 50},
                  {:first_name=>"Clark", :score => 45, :minutes => 20}]

player_scores2 = [{:last_name=>"Wayne", :points => 13, :time => 40},
                  {:last_name=>"Kent", :points => 3, :minutes => 20}]

I'd like to create a new array of hashes which adds up :score and :points together and assign it to a key called :score. I'd also like to combine the :first_name and :last_name and assign it to a key called :full_name. I want to discard any other keys.

This would result in this array:

all_players = [{:full_name => "Bruce Wayne", :score => 56}, 
               {:full_name => "Clark Kent", :score => 48}]

Is there an elegant way to do this?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Chanpory
  • 3,015
  • 6
  • 37
  • 49

4 Answers4

8

Something like this:

player_scores1.zip(player_scores2).map { |a,b|
    {
        :full_name => a[:first_name]+' '+b[:last_name],
        :score => a[:score]+b[:points]
    }
}
Nakilon
  • 34,866
  • 14
  • 107
  • 142
  • Cool, had not heard of the zip method before! – Chanpory Dec 07 '10 at 23:20
  • This function http://en.wikipedia.org/wiki/Convolution_(computer_science) exists in a lot of languages: Python, Haskell... – Nakilon Dec 07 '10 at 23:27
  • 2
    +1, but I think it's idiomatic Ruby to use do-end on multi-line blocks. – tokland Dec 08 '10 at 00:09
  • @tokland, `{}` != `do end`. Sometimes you need `{}`. For example, when I tested this code, I pasted `p` before it. With `do end` it will return enumerator and print smth like `<#Enumerator...` instead of array. – Nakilon Dec 08 '10 at 02:29
  • @Nakilon what version of ruby are you using? It's working perfectly normal with `do end` for me on 1.8.7 – mpd Dec 08 '10 at 03:24
  • `{}` and `do|end` are similar but not exactly the same. They differ in their precedence, which can cause differences in how Ruby binds parameters. – the Tin Man Dec 08 '10 at 03:36
  • @Greg, how does precedence affect on difference between `p x.map{}` and `p x.map do end`? – Nakilon Dec 08 '10 at 03:53
  • That example is too simple. A method with parameters followed by a block could have problems if parenthesis are not used to wrap the parameters. `{` would bind to the last parameter where `do` won't. See [Ruby block and unparenthesized arguments](http://stackoverflow.com/questions/420147/ruby-block-and-unparenthesized-arguments) – the Tin Man Dec 08 '10 at 04:07
  • @Nakilon, @Greg, indeed, you usually need to surround with parenthesis a call that contains a do-end block inside: p(x.map do ... end), but that's IMHO just a minor inconvenience that does not invalidate the idiomatic uses. – tokland Dec 08 '10 at 09:29
0

The code you're looking for is:

final = []
player_scores1.each_index do |index|
  entry_1 = player_scores1.values(index)
  entry_2 = player_scores2.values(index)[:first_name]
  score = entry_1[:score] + entry_2[:points]
  final << {:full_name => "#{entry_1[:first_name]} #{entry_2[:last_name]}", :score => score }
end

Any suggestions on tightening this up would be much appreciated!

Sam Ritchie
  • 10,988
  • 4
  • 42
  • 53
0

This works. I don't if that's elegant enough though.

player_scores1 = [{:first_name=>"Bruce", :score => 43, :time => 50},
                  {:first_name=>"Clark", :score => 45, :minutes => 20}]

player_scores2 = [{:last_name=>"Wayne", :points => 13, :time => 40},
                  {:last_name=>"Kent", :points => 3, :minutes => 20}]

p (0...[player_scores1.length, player_scores2.length].min).map {|i| {
    :full_name => player_scores1[i][:first_name] + " " + player_scores2[i][:last_name], 
    :score => player_scores1[i][:score] + player_scores2[i][:points]
}}

This example on Codepad.

detunized
  • 15,059
  • 3
  • 48
  • 64
0

This uses zip with a block to loop over the hashes, joining the names and summarizing:

all_players = []
player_scores1.zip(player_scores2) { |a, b| 
  all_players << { 
    :full_name => a[:first_name] + ' ' + b[:last_name],
    :score     => a[:score] + b[:points]
  } 
}
all_players # => [{:full_name=>"Bruce Wayne", :score=>56}, {:full_name=>"Clark Kent", :score=>48}]
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • 1
    init an empty array + append values in a loop + return the array = map – tokland Dec 08 '10 at 09:30
  • Usually. This cuts out the usual extra step people would do of `zip.each` by letting the block for zip do it. There's no point in doing one more iteration over the hashes since zip is already doing a loop over each of them. – the Tin Man Dec 08 '10 at 09:36