Lucasio
Lucasio

Reputation: 29

Array of arrays merge in Rails

I have an array of arrays like this: [['Area1', 12345], ['Area2', 54321]].

So if I will add to the existing array this array ['Area1', 33441] I want either to merge the array like this [['Area1', 12345, 33441], ['Area2', 54321]] or to display the information like this:

Area1: 12345, 33441 Area2: 54321

Does anyone have any idea if is this possible in rails? Thank you!

Upvotes: 0

Views: 1893

Answers (4)

Cary Swoveland
Cary Swoveland

Reputation: 110675

For

arr = [['Area1', 12345], ['Area2', 54321]]
to_add = ['Area1', 1212]

we may compute the desired array as follows:

(arr + [to_add]).group_by(&:first).map { |k,v| [k, *v.map(&:last)] }
  #=> [["Area1", 12345, 1212], ["Area2", 54321]]

The first step, using Enumerable#group_by produces the following hash:

(arr + [to_add]).group_by(&:first)
  #=> {"Area1"=>[["Area1", 12345], ["Area1", 1212]],
  #    "Area2"=>[["Area2", 54321]]} 

For this to work we must be sure that group_by produces a hash whose keys are ordered the same as arr.map(&:first).uniq #=> ["Area1", "Area2"]. (Note that Array#uniq preserves order by keeping the first instance of each element). See this SO question. As Array#each generates the elements of arr + [to_add] in order, and the insertion order of hash keys is maintained for Ruby versions 1.9+, we can be assured that the result will have elements in the correct order.

Others have argued that representing the data as a hash may be a better data model. That may be, but the question does not provide enough information about how the return value is to be used to be certain about that. I've therefore provided only what was asked for by the question.

Upvotes: 0

jvillian
jvillian

Reputation: 20263

If you would like to stick with arrays, then try something like:

arr1 = [['Area1', 12345], ['Area2', 54321]]
arr2 = ['Area1', 33441]
arr1.inject([]){|r,i| r.push(i[0] == arr2[0] ? i.push(arr2[1]) : i)}

In console:

2.3.1 :001 > arr1 = [['Area1', 12345], ['Area2', 54321]]
 => [["Area1", 12345], ["Area2", 54321]] 
2.3.1 :002 > arr2 = ['Area1', 33441]
 => ["Area1", 33441] 
2.3.1 :003 > arr1.inject([]){|r,i| r.push(i[0] == arr2[0] ? i.push(arr2[1]) : i)}
 => [["Area1", 12345, 33441], ["Area2", 54321]] 

Looking at polmiro's answer, I prefer his map to my inject. They are essentially the same, I believe. Except that map does the work for you of creating a new array whereas with inject you have to pass in the new array. And, map is doing an implicit push whereas, with inject, again, you have to do the work yourself. Nice job, polmiro!

Here's the one-line version of his answer (if you like that sort of thing):

arr1.map{|a| a[0] == arr2[0] ? a << arr2[1] : a}   

And again in console:

2.3.1 :001 > arr1 = [['Area1', 12345], ['Area2', 54321]]
 => [["Area1", 12345], ["Area2", 54321]] 
2.3.1 :002 > arr2 = ['Area1', 33441]
 => ["Area1", 33441] 
2.3.1 :003 > arr1.map{|a| a[0] == arr2[0] ? a << arr2[1] : a}    
 => [["Area1", 12345, 33441], ["Area2", 54321]]      

Upvotes: 0

xlembouras
xlembouras

Reputation: 8295

You could use Hash#merge! method with a block for that:

initial = [['Area1', 12345], ['Area2', 54321]].to_h
initial.merge!({'Area1' => 1212 }) { |_,v1,v2| [v1,v2].flatten }
#=> {"Area1"=>[12345, 1212], "Area2"=>54321}

and if you want the result to be an array, you can use

initial.merge!({'Area1' => 1212 }) { |_,v1,v2| [v1,v2].flatten }.to_a

Upvotes: 4

polmiro
polmiro

Reputation: 1986

You're probably not using the right data structure. You are better of using a Hash for the purpose that looks like this:

  areas = {
    'Area1' => [12345], 
    'Area2' => [54321]
  }

But following up your structure you can do something like this:

  areas = [['Area1', 12345], ['Area2', 54321]]
  new_area = ['Area1', 33441]

  areas.map { |area|
    if area.first == new_area.first
      area + [new_area.last]
    else
      area
    end
  end

Also just so it is easier for you to find this in the future, this has not thing to do with Rails (the web framework that sits on top of Ruby) but with Ruby itself (the programming language).

Upvotes: 0

Related Questions