0

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!

Lucasio
  • 29
  • 10

4 Answers4

4

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
xlembouras
  • 8,215
  • 4
  • 33
  • 42
  • Yes, this is perfect for my case. Thank you a lot @xlembouras ! – Lucasio May 23 '17 at 20:12
  • I expect the return value would be easier to work with if `"Area2"=>54321` were `"Area2"=>[54321]` (to be consistent with arrays of two or more values). For example, `initial = [['Area1', 12345], ['Area2', 54321]]; (initial + [['Area1', 1212]]).each_with_object({}) { |(area,nbr),h| h.merge!(area=>[nbr]) { |_,v1,v2| v1 + v2 } } #=> {"Area1"=>[12345, 1212], "Area2"=>[54321]}`. (Readers: both x's answer and this variant use the form of [Hash#merge!](http://ruby-doc.org/core-2.4.0/Hash.html#method-i-merge-21) that employs a block to compute the values of keys present in both hashes being merged.) – Cary Swoveland May 26 '17 at 06:30
0

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).

polmiro
  • 1,966
  • 15
  • 22
0

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]]      
jvillian
  • 19,953
  • 5
  • 31
  • 44
0

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.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100