0

Hey ,Guys ,I have a question. I want to transform the array.[[1, [-1, 1]], [1, [20, 8]], [1, [30, 4]], [1, [40, 2]], [1, [41, 6]], [1, [70, 243]]] into this style [1,[[-1,1],[20,8],[30,4]...] or a hash [1=>...] How can i do this trick? thank you !

3 Answers 3

6

Dict method:

array = [your array]
dict = {}
array.each{ |a| (dict[a[0]] ||= []) << a[1] }

For clarity Chuck's suggestion would bring this to:

array = [your array]
dict = Hash.new {|h,k| h[k] = []}
array.each{ |a| dict[a[0]] << a[1] }

You can then get an array from this in the style you want by doing:

new_arr = dict.select{|k,v| [k, v]}

Which will give you:

[[1, [[-1, 1], [20, 8], [30, 4], [40, 2], [41, 6], [70, 243]]]]

Notice the extra array, because if you had arrays begining with 2 you'd have another set at the end. So new_arr[0] will give you the array you were originally looking for.

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

2 Comments

You could also easily derive the array equivalent this way. Create the hash, then do hash.map {|key, values| [key, values]}. Also, you can greatly simplify your each by creating the hash as Hash.new {[]} — then you don't need to to the ||= [] on each iteration, since nonexistent keys will return an empty array.
Hash.new{[]} would only work with assignment operators like +=, but with << it won't. I edited the answer accordingly.
2

If you want it as a Hash, it's simply

h = Hash[ary.group_by(&:first).map {|k, v| [k, v.map(&:last)] }]

And if you want it as an Array, you just convert the Hash to an Array:

a = *h

3 Comments

+1 for a very elegant solution, but I'd like to mention that group_by may not available with older ruby APIs.
I believe .group_by is ruby 1.9 though there may be a rails option.
The code runs unmodified in Ruby 1.8.7 and newer. To use it in Ruby 1.8.6, you'll have to require backports for Enumerable#group_by and Symbol#to_proc.
0

You can do this:

array = [[1, [-1, 1]], [1, [20, 8]], [1, [30, 4]],
         [1, [40, 2]], [1, [41, 6]], [1, [70, 243]]]

# map into hashes that can be merged together
hashes = array.map do |key,value|
  { key => [value] }
end

# fold all hashes into one hash by merging the values by key
merged_hash = hashes.inject({}) do |accu,value|
  accu.merge!(value) {|_,o,n| o|n }
end

This can be written as a not so easy one-liner:

array.map{|k,v|{k=>[v]}}.inject({}){|a,v|a.merge!(v){|_,o,n|o|n}}
#==> {1=>[[-1, 1], [20, 8], [30, 4], [40, 2], [41, 6], [70, 243]]}

2 Comments

This seems more complicated than Matt's solution for no benefit. Why do you prefer this way of doing it?
@Chuck It solves the problem without polluting the local variable namespace by using inject. And I prefer the merge method over ||=[]. Of course this is a matter of taste. But I think Matt's solution could be rewritten using inject, too, thus preventing to pollute the namespace with temp vars.

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.