Reputation: 29
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
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
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
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
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