4

I have an array of hashes:

[{:foo => 1, :bar => 2}, {:foo => 2, :bar => 4} ...]

And an array of integers:

[3, 6]

I want combine the values from the integer array and the hashes to end up with something like:

[{:foo => 1, :bar => 2, :baz => 3}, {:foo => 2, :bar => 4, :baz => 6}]

I am currently doing this:

myArrayOfHashes.each_with_index |myHash, index|
    myHash[:baz] = myArrayOfIntegers[index]
end

Is that the right approach?

I was imagining a more functional approach where I iterate over both arrays simultaneously, like something using zip + map.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
sberry
  • 128,281
  • 18
  • 138
  • 165
  • It's not necessary to repeatedly specify you want to do this with Ruby. The tags you set will tell us the language and features or libraries. – the Tin Man Jun 21 '13 at 07:02
  • Yeah, good call @theTinMan. Just never sure if favorite tags are used by the person who could answer. But, noted for the future. – sberry Jun 21 '13 at 07:06
  • 1
    If someone tries to answer who *hasn't* paid attention to the tags, the odds are really good they'll get plenty of down-votes for not having paid attention. They won't repeat that. – the Tin Man Jun 21 '13 at 07:08
  • 1
    Your current implementation looks good enough for me already – John Dvorak Jun 21 '13 at 07:09
  • @JanDvorak +1 for efficiency and less memory consumption. – Arie Xiao Jun 21 '13 at 07:22

3 Answers3

6

Try:

require 'pp'

ary_of_hashes = [{:foo => 1, :bar => 2}, {:foo => 2, :bar => 4}]
[3, 6].zip(ary_of_hashes).each do |i, h|
  h[:baz] = i
end

pp ary_of_hashes

Which results in:

[{:foo=>1, :bar=>2, :baz=>3}, {:foo=>2, :bar=>4, :baz=>6}]

zip is a good tool for this, but map won't really buy much, at least nothing that you can't do as easily with each in this case.

Also, don't name variables using CamelCase like myArrayOfHashes, instead use snake_case, like ary_of_hashes. We use CamelCase for class names. Technically we can used mixed case for variables, but by convention we don't do that.

And, it's possible to use each_with_index, but it results in awkward code, because it forces you to use an index into [3, 6]. Let zip join the respective elements of both arrays and you'll have everything you need to massage the hash.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • Try `ri Kernel.pp` from the command-line, or check http://www.ruby-doc.org/stdlib-2.0/libdoc/pp/rdoc/index.html – the Tin Man Jun 21 '13 at 07:13
  • Very good. I figured I could use zip here and do exactly this, not sure what mistake I was making when I tried it earlier. – sberry Jun 21 '13 at 07:20
  • Yeah, I also follow those conventions when actually writing code. I am not sure why I decided to write my sample like that - I guess too much scala recently. – sberry Jun 21 '13 at 07:23
6

map is useful when you want to leave the original objects intact:

a = [{:foo => 1, :bar => 2}, {:foo => 2, :bar => 4}]
b = [3,6]
a.zip(b).map { |h, i| h.merge(baz: i) }
# => [{:foo=>1, :bar=>2, :baz=>3}, {:foo=>2, :bar=>4, :baz=>6}]
a.inspect
# => [{:foo=>1, :bar=>2}, {:foo=>2, :bar=>4}]
toro2k
  • 19,020
  • 7
  • 64
  • 71
  • 2
    According to the code sample in the question, it should be `a.zip(b).each {|h, i| h.update(baz: i)}` rather than creating a new Hash and leaving `a` unchanged. – Arie Xiao Jun 21 '13 at 07:15
  • I may have been over-reaching with looking to use map since it looks like it might not be necessary. + 1 still though. – sberry Jun 21 '13 at 07:22
3
array_of_hashes.each { |hash| hash.update baz: array_of_integers.shift }
Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74