0

I have a 2 dimensional array

v = [ ["ab","12"], ["ab","31"], ["gh","54"] ]

The first element of the subarray of v will have repeating elements, such as "ab". I want to create a hash that puts the key as the first element of the subarray, and values as an array of corresponding second elements from v.

please advice.

Further, I want this, h={"ab"=>["12","31"],"gh"=>["54"]} and then I want to return h.values, such that the array [["12","31"],["54"]] is returned

3 Answers 3

4
v.inject(Hash.new{|h,k|h[k]=[]}) { |h, (k, v)| h[k] << v ; h}

What it does:

  • inject (also called reduce) is a fold. Wikipedia defines folds like this: "a family of higher-order functions that analyze a recursive data structure and recombine through use of a given combining operation the results of recursively processing its constituent parts, building up a return value".

  • The block form of Hash.new takes two arguments, the hash itself and the key. If your default argument is a mutable object, you have to set the default this way, otherwise all keys will point to the same array instance.

  • In inject's block, we get two arguments, the hash and the current value of the iteration. Since this is a two element array, (k, v) is used to destructure the latter into two variables.

  • Finally we add each value to the array for its key and return the entire hash for the next iteration.

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

6 Comments

If this is the answer you were looking for you should accept it with the little tick mark below the up-/downvote arrows.
I undertand most part of the code. Please explain this. In the inject>parameter>block what is the h,k and how it works?
It's explained in the third bullet point... inject's second argument would be the current element of the collection v. Since those are two element arrays themselves, I split them up in two different variables, called k (for key) and v (for value), but of course the names are arbitrary.
And when I do (v.inject(Hash.new{|h,k|h[k]=[]}) { |h, (k, v)| h[k] << v ; h}).values, it only returns the last value. Why?
It returns [["12", "31"], ["54"]] for me, are you sure you are using the same test data?
|
1
v.inject({­}) do |res,­ ar|
  res[ar.fir­st] ||= []
  res[ar.fir­st] << ar.la­st
  res
end

1 Comment

I want this, h={"ab"=>["12","31"],"gh"=>["54"]} and then I want to return h.values, such that the array [["12","31"],["54"]] is returned.
1
v = [ ["ab","12"], ["ab","31"], ["gh","54"] ]

This gets you a hash, where the keys are the unique first elements from the sub arrays.

h = v.inject({}) { |c,i| (c[i.first] ||= []) << i.last; c }

This turns that hash back into an array, just in case you need the array of arrays format.

arr = h.collect { |k,v| [k,v] }

2 Comments

Hey Ctcherry, please explain what's happening here. I am new to ruby.
Hey Ctcheery, what this does is h={"ab"=>"12","ab"=>"31","gh"=>"54"} but what I want is this h={"ab"=>["12","31"],"gh"=>["54"]} and then I want to return h.values, such that the array [["12","31"],["54"]] is returned.

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.