1

I have this data structure resulted from a query grouping

{
  [0, "AR"]=>2,
  [0, nil]=>1,
  [0, "AQ"]=>6,
  [1, nil]=>4,
  [1, "AQ"]=>3,
  [2, "BG"]=>1,
  [2, nil]=>1,
}

I want to manipulate it so I end up with a structure grouped like this

{
  0 => {
    'AR' => 2,
    'AQ' => 6,
    nil => 1
  },
  1 => {
    'AQ' => 1,
    nil => 4
  },
  2 => {
    'BG' => 1,
    nil => 1
  }
}
0

4 Answers 4

2
input = {
  [0, "AR"]=>2,
  [0, nil]=>1,
  [0, "AQ"]=>6,
  [1, nil]=>4,
  [1, "AQ"]=>3,
  [2, "BG"]=>1,
  [2, nil]=>1,
}

result = {}

input.each do |k, v|
  if result[k[0]]
    result[k[0]].merge!({ k[1] => v })
  else
    result[k[0]] = { k[1] => v }
  end
end

puts result 
#{0=>{"AR"=>2, nil=>1, "AQ"=>6}, 1=>{nil=>4, "AQ"=>3}, 2=>{"BG"=>1, nil=>1}}

I think this is not the most succinct way, I hope some advice!

Sign up to request clarification or add additional context in comments.

Comments

2
hash = {
  [0, "AR"]=>2,
  [0, nil]=>1,
  [0, "AQ"]=>6,
  [1, nil]=>4,
  [1, "AQ"]=>3,
  [2, "BG"]=>1,
  [2, nil]=>1,
}

new_hash = {}

hash.each{|k, v| new_hash[k[0]] ||= {}; new_hash[k[0]].merge!({k[1] => v})}

puts  new_hash # {0=>{"AR"=>2, nil=>1, "AQ"=>6}, 1=>{nil=>4, "AQ"=>3}, 2=>{"BG"=>1, nil=>1}}

1 Comment

conciser than mine!
2

Here is one more very similar to previous answers but with using of #each_with_object:

hash = {
  [0, "AR"]=>2,
  [0, nil]=>1,
  [0, "AQ"]=>6,
  [1, nil]=>4,
  [1, "AQ"]=>3,
  [2, "BG"]=>1,
  [2, nil]=>1,
}

result_hash = Hash.new { |h,k| h[k] = {} }
hash.each_with_object(result_hash) do |((parrent_key, key), value), res|
  res[parrent_key].merge!(key => value)
end

=> {0=>{"AR"=>2, nil=>1, "AQ"=>6}, 1=>{nil=>4, "AQ"=>3}, 2=>{"BG"=>1, nil=>1}}

Comments

1

I came up with an answer that doesn't require additional variable assignments in its enclosing scope (it has "referential transparency": https://en.wikipedia.org/wiki/Referential_transparency)

input
.group_by { |(arr, num)| arr.first }
.each_with_object(Hash.new) do |(key, vals), hsh|
  vals.each do |((key, innerkey), innerval)|
    hsh[key] ||= {}
    hsh[key][innerkey] = innerval
  end
  hsh
end
# {0=>{"AR"=>2, nil=>1, "AQ"=>6}, 1=>{nil=>4, "AQ"=>3}, 2=>{"BG"=>1, nil=>1}}

Two high-level steps:

  1. I noticed the output object is grouped by the first array element (here, 0/1/2). I use #group_by to create a hash with that structure.
# output of `#group_by` on first array element:
key: 0, vals: [ [[0, "AR"], 2],  [[0, nil],  1],  [[0, "AQ"], 6] ]
key: 1, vals: [ [[1, nil],  4],  [[1, "AQ"], 3]                  ]
key: 2, vals: [ [[2, "BG"], 1],  [[2, nil],  1]                  ]
  1. I use #each_with_object to construct the nested hashes. For each vals array above, I extracted the second and third values by destructuring the arrays in the block parameter (((key, innerkey), innerval)) and then the hash assignment was straightforward.

1 Comment

elegant solution

Your Answer

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.