1

I have a hash which is named h. I want to store the contents in a multidimensional array named ar. I am getting the error no implicit conversion from nil to integer.

Here is my code:

h = {"bob" => {email: "abc" , tel: "123"} , "daisy" => {email: "cab" , tel: "123456"}}

keys = h.keys

l = h.length 

ar = Array.new(l) { Array.new(3) }

for i in 0..l-1
  ar[[2][i]] = keys[i]
  ar[[1][i]] = h[keys[i]][:email]
  ar[[0][i]] = h[keys[i]][:tel]
end

puts ar.to_s

The desired output is:

[[email_1, email_2, ..][tel_1, tel_2, ..][name_1, name_2, ..]]

For example:

[["abc", "cab"] , ["123", "123456"] , ["bob", "daisy"]]

4 Answers 4

2

This is the way I would handle this:

h.values.each_with_object({}) do |h,obj|
  obj.merge!(h) { |_k,v1,v2| ([v1] << v2).flatten }
end.values << h.keys
#=> [["abc", "cab"], ["123", "123456"], ["bob", "daisy"]]
  • First grab all the values (as Hashes)
  • loop through them with an accumulator ({})
  • merge! the values into the accumulator and on conflict append them to an array
  • return the values from the accumulator
  • then append the original keys

This is less explicit than @mudasobwa's answer and relies on the order of the first value to determine the output. e.g. if :tel came before :email the first 2 elements would have a reversed order

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

2 Comments

Your code works. Can you please suggest a way to improve mine or tell me where i am going wrong
@AkshatBansal let's start with what you think ar[[2][0]] represents because it actually means ar[2] because you are creating an Array ([2]) and then calling Array#[] on it thus [2][0] #=> 2 the problem here is that [2][1] #=> nil so ar[[2][1]] is equivalent to ar[nil] which raises the error on the second iteration. what you meant was ar[2][0] and ar[2][1] but that also has a mistake as ar = [[nil,nil,nil],[nil,nil,nil]] (notice no ar[2] only ar[0] and ar[1].
2

[2][i] returns nil for i > 0. ar[nil] raises the exception.

Here is what you do:

arr = h.map { |k, v| [v[:email], v[:tel], k] }.reduce(&:zip)

To make your code work:

Change

ar = Array.new(l) { Array.new(3) }

To

ar = Array.new(3) { Array.new(l) }

Change

ar[[2][i]] = keys[i]
ar[[1][i]] = h[keys[i]][:email]
ar[[0][i]] = h[keys[i]][:tel]

To

ar[2][i] = keys[i]
ar[1][i] = h[keys[i]][:email]
ar[0][i] = h[keys[i]][:tel]

3 Comments

Sorry i did not got your point , can you please tell what should i do to correct it
It is not giving error but also not giving the output needed.
+1 for explaining the problem with the OPs solution but please note your proposed solution only works with 2 keys in the main hash otherwise the output gains increasing depth
1

What you mostly should do is to stop writing PHP code with Ruby syntax. Here it’s how is to be done in Ruby:

h.map { |k, v| [v[:email], v[:tel], k] }.reduce(&:zip)

or, even better, if you are certain of elements order in nested hashes:

h.map { |k, v| [*v.values, k] }.reduce(&:zip).map(&:flatten)

All the methods map, reduce and zip are thoroughly described in the documentation.

8 Comments

Your "even better" relies on the need for the elements to be in the same order where as your first example (which is better) does not
@engineersmnky ...which is precisely described in the comment to “even better,” isn’t it?
describing a caveat does not make a solution "better" though does it? What makes it better? less key strokes?
@engineersmnky indeed, I am always forgetting who does flatten the result and who does not. It would work for any amount of keys with .map(&:flatten) afterwards.
@engineersmnky always welcome; The former one is likely the exact solution for the input given. The latter is the generalized. :)
|
0
h.map { |k, v| [*v.values_at(:email, :tel), k] }.transpose
  #=> [["abc", "cab"], ["123", "123456"], ["bob", "daisy"]]

The intermediate calculation is as follows.

h.map { |k, v| [*v.values_at(:email, :tel), k] }
  #=> [["abc", "123", "bob"], ["cab", "123456", "daisy"]]

Comments

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.