Matt White
Matt White

Reputation: 117

Group by hash in Ruby

My goal is to take a hash of names and numbers, for example:

hash = {
  "Matt" => 30,
  "Dave" => 50,
  "Alex" => 60
}

and to group them by whether they achieved a "passing" score. I'd like the results to be passed as an array into two separate keys, say :pass and :fail like this:

hash = { "pass" => ["Alex", 60], "fail" => [["Matt", 30]["Dave",60]]}

I know the group_by method is what I need, but am not sure as to how I would pass the values into the new keys.

The passing grade should be decided by the user. For this example, you could use 45.

Upvotes: 1

Views: 1331

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110685

Here are two ways:

#1

a = hash.to_a 
{ "pass" => a.select { |_,v| v > 50 }, "fail" => a.reject { |_,v| v > 50 } }
  #=> {"pass"=>[["Alex", 60]], "fail"=>[["Matt", 30], ["Dave", 50]]}

#2

[:pass, :fail].zip(hash.to_a.partition { |_,v| v > 50 }).to_h
  #=> {:pass=>[["Alex", 60]], :fail=>[["Matt", 30], ["Dave", 50]]} 

These both give the return values as arrays of tuples, which you wanted for "fail" but not for "pass". Wouldn't that make it a pain to work with? Is it not better for both to be arrays of tuples?

Consider returning hashes for values:

 {"pass" => hash.select { |_,v| v > 50 }, "fail" => hash.reject {|_,v| v > 50 }}
   #=> {"pass"=>{"Alex"=>60}, "fail"=>{"Matt"=>30, "Dave"=>50}}

Would that not be more convenient?

Upvotes: 0

Alex Tonkonozhenko
Alex Tonkonozhenko

Reputation: 1574

You can do somthing this in such way:

PASSING_GRADE = 45
hash.group_by {|_, v| v >= PASSING_GRADE ? 'pass' : 'fail'}

Here is result:

{"fail"=>[["Matt", 30], "pass"=>[["Alex", 60], ["Dave", 50]]]}

Upvotes: 6

Jake Shorty
Jake Shorty

Reputation: 727

You could simply do something like this:

sorted = {"pass" => [], "fail" => []}
hash.each do |name, grade|
  if grade >= PASSING_GRADE
    sorted["pass"] << [name, grade]
  else
    sorted["fail"] << [name, grade]
  end
end

Upvotes: 1

Related Questions