8
\$\begingroup\$

Given two arrays (for instance a list of people and a list of subjects), I need to initialise a Hash with all zeros for all possible combinations of these two arrays. I can manage to do that with the code on the bottom, but it seems like there should be a less convoluted, more elegant way to do this.

people = %w(tom mary rob)
subjects = %w(math english)
Hash[people.map { |person| [person, Hash[subjects.map { |subject| [subject, 0] }]]}]
# output: => {"tom"=>{"math"=>0, "english"=>0},
# "mary"=>{"math"=>0, "english"=>0},
# "rob"=>{"math"=>0, "english"=>0}}
asked Sep 25, 2015 at 18:04
\$\endgroup\$
0

3 Answers 3

5
\$\begingroup\$

Conceptually, that's the way to go. I'd just propose some small changes:

  • Use Array#to_h instead of Hash#[]
  • This line is too long, split it in multiple lines.

So I'd write:

scores = people.map do |person|
 person_scores = subjects.map { |subject| [subject, 0] }.to_h 
 [person, person_scores]
end.to_h
answered Sep 25, 2015 at 20:30
\$\endgroup\$
4
\$\begingroup\$

While you can do this in one line, there's really no need. At least you can break the line into do..end blocks to keep things more readable.

But moreso, I'd consider duping the zeros hash (presumably grades for the subjects so I'll call it that) once you've built it, rather than build and re-build it for each person.

Also, you can use each_with_object (a sort of reduce) instead of wrapping a mapped array in Hash[..].

For instance,

grades = subjects.each_with_object({}) { |subject, hash| hash[subject] = 0 }
people.each_with_object({}) { |name, hash| hash[name] = grades.dup }

But there are many ways to go about this. While it's a little outside the task, you could simply initialize a Hash with default value of zero for unknown keys:

grades = Hash.new(0)
grades["math"] #=> 0
grades["no a real subject"] #=> 0

Of course, as the last line shows, you'll get zero for any key, even if it doesn't really make sense. Usually you'd of course just get a nil. You'll also want to be careful with this approach in general, if the default value is an object, since it won't automatically dup itself; it'll be the same object (same object reference) for every key. It's not a problem with numbers though.

Still, it'd condense things to one line again:

people.each_with_object({}) { |name, hash| hash[name] = Hash.new(0) }

A more precise alternative, that only creates the required keys, could be:

grades = Hash[ subjects.zip(Array.new(subjects.size, 0)) ]

And of course, recent versions of Ruby have a to_h method that lets you chain the call, rather than wrap an array in it:

grades = subjects.map { |subject| [subject, 0] }.to_h
people.map { |name| [name, grades.dup] }.to_h

Which is close to what you started with, just using dup.

There are even more ways to do things - this is just off the top of my head.

answered Sep 25, 2015 at 20:25
\$\endgroup\$
2
\$\begingroup\$

I did start with this:

~/ (main) > notes = Array.new(subjects.size,0)
=> [0,0]
~/ (main) > Hash[[people, Array.new(people.size, Hash[[subjects, notes].transpose])].transpose]
=> {
 "tom" => {
 "math" => 0,
 "english" => 0
 },
 "mary" => {
 "math" => 0,
 "english" => 0
 },
 "rob" => {
 "math" => 0,
 "english" => 0
 }
}

But there is a pattern: make a hash with keys from a supplied array and all with the same value. Then a lambda could help:

~/ (main) > head_val_to_h = ->(head, val) { Hash[[head, Array.new(head.size, val)].transpose]}
=> #<Proc:0x00000008bce460@(pry):55 (lambda)>
~/ (main) > head_val_to_h[people, head_val_to_h[subjects, 0]]
=> {
 "tom" => {
 "math" => 0,
 "english" => 0
 },
 "mary" => {
 "math" => 0,
 "english" => 0
 },
 "rob" => {
 "math" => 0,
 "english" => 0
 }
}
answered Oct 4, 2015 at 0:49
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.