565

I tried:

somearray = ["some", "thing"]
anotherarray = ["another", "thing"]
somearray.push(anotherarray.flatten!)

I expected

["some", "thing", "another", "thing"]

but got

["some", "thing", nil]
David McKee
  • 170
  • 8
ncvncvn
  • 5,683
  • 2
  • 16
  • 4
  • 6
    It's worth saying (not to give you grief, but because it will bite you again and again) that your expectation is the problem here. Ruby arrays (unlike say arrays in Perl) do *not* automatically flatten in contexts like this. This isn't a bug: it's a feature. – Telemachus Nov 26 '09 at 11:45
  • 4
    `ri Array@flatten!` Why this question is getting so many votes? The doc is explicit `Array#flatten!` _Flattens self in place. Returns nil if no modifications were made (i.e., the array contains no subarrays.)_ – yeyo Jan 31 '16 at 15:39
  • 14
    Questions get upvotes if they are useful to users. The simplest questions get the most upvotes because they are useful to the most people. – Ziggy Jan 09 '18 at 03:09
  • @yeyo, don't you just think that flatten operation is free? – Konstantin Feb 08 '18 at 18:53
  • @Konstantin op isn't looking for alternatives or talking about performance issues, op was expecting a result he or she didn't get because `flatten!` doesn't work like that. Finally, the question reflects a logic problem rather than an optimization problem. See pilcrow's answer below for more. – yeyo Feb 09 '18 at 13:41
  • @yeyo, well the actual question title doesn't completely correlate to the question title. I assume that OP actually wanted to add an array to another one, so I believe it's worthy to clarify dos and donts both to him and all the newcomers. You know, SO question title has a big weight in SEs ;) – Konstantin Feb 12 '18 at 21:13

18 Answers18

822

You've got a workable idea, but the #flatten! is in the wrong place -- it flattens its receiver, so you could use it to turn [1, 2, ['foo', 'bar']] into [1,2,'foo','bar'].

I'm doubtless forgetting some approaches, but you can concatenate:

a1.concat a2
a1 + a2              # creates a new array, as does a1 += a2

or prepend/append:

a1.push(*a2)         # note the asterisk
a2.unshift(*a1)      # note the asterisk, and that a2 is the receiver

or splice:

a1[a1.length, 0] = a2
a1[a1.length..0] = a2
a1.insert(a1.length, *a2)

or append and flatten:

(a1 << a2).flatten!  # a call to #flatten instead would return a new array
pilcrow
  • 56,591
  • 13
  • 94
  • 135
  • 59
    Using push instead of concat avoids the creation of a third array, so this is preferred for large arrays. – phatmann Jun 15 '12 at 13:54
  • 1
    +1 @phatmann. I've edited to reflect that push/unshift are really distinct from concatenation which (you're right) does suggest to me the creation of a new array. – pilcrow Jun 15 '12 at 14:15
  • You are AWESOME, you know that, pilcrow? It never occurred to me that I was getting a multi-dimensional array when pushing one array to another and didn't think this is the source of my error. Thank you! – sk4l Jul 17 '13 at 02:02
  • 18
    @phatmann Concatenation with [`Array#concat`](http://www.ruby-doc.org/core/Array.html#method-i-concat) does not allocate a new array, Concatenation with [`Array#+`](http://www.ruby-doc.org/core/Array.html#method-i-2B) does – cbliard Jul 21 '14 at 09:19
  • 1
    @cbliard Now that I look at the docs I see you are right about `concat`! So really the answer should be edited yet again to just have `concat` as the solution. The other solutions are cool but superfluous. – phatmann Jul 22 '14 at 18:08
  • At least in 1.9.3, `+=` behaves as you'd expect: ```1.9.3-p484 :001 > a1 = [1,2,3] => [1, 2, 3] 1.9.3-p484 :002 > a2=[4,5,6] => [4, 5, 6] 1.9.3-p484 :003 > a1 += a2 => [1, 2, 3, 4, 5, 6] 1.9.3-p484 :004 > a1 => [1, 2, 3, 4, 5, 6]``` – erik258 Aug 28 '14 at 02:50
  • @DanFarrell, right, we know that works as commented in the `a1 + a2` example. – pilcrow Aug 28 '14 at 14:35
  • 7
    The only thing this answer is missing is benchmark comparisons of each approach. +1! – Terra Ashley Sep 07 '15 at 02:49
  • `[a1, a2].reduce(:+)` works well, especially if you may have more than 2 elements in the list – ScottJ Mar 24 '17 at 22:43
  • Can we explain the asterisk operator in this situation? – Justus Eapen Apr 05 '17 at 16:14
  • **Note**: `([1] << 3).flatten` will produce `[1,3]`, while `([1] << 3).flatten!` will produce **`nil`**. – Developer Marius Žilėnas Jan 23 '18 at 12:27
  • 1
    *Note*: concat will modify original array. `a = [1, 3]` `a.concat([4, 5])`, a becomes `[1, 3, 4, 5]` – vaibhavatul47 Jul 26 '18 at 15:06
  • @pilcrow what is this a1.push(*a2) = > asterisk used for? what it means and were all we can use this symbol? I am new to ruby. – ajay_speed Oct 12 '22 at 09:11
  • @ajay_speed, the asterisk or "splat" is a Ruby operator. Check the docs, look around here, and ask a question here if it doesn't make sense. – pilcrow Oct 12 '22 at 13:04
226

You can just use the + operator!

irb(main):001:0> a = [1,2]
=> [1, 2]
irb(main):002:0> b = [3,4]
=> [3, 4]
irb(main):003:0> a + b
=> [1, 2, 3, 4]

You can read all about the array class here: http://ruby-doc.org/core/classes/Array.html

Max
  • 15,157
  • 17
  • 82
  • 127
micmoo
  • 5,991
  • 3
  • 22
  • 16
85

The cleanest approach is to use the Array#concat method; it will not create a new array (unlike Array#+ which will do the same thing but create a new array).

Straight from the docs (http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat):

concat(other_ary)

Appends the elements of other_ary to self.

So

[1,2].concat([3,4])  #=> [1,2,3,4]  

Array#concat will not flatten a multidimensional array if it is passed in as an argument. You'll need to handle that separately:

arr= [3,[4,5]]
arr= arr.flatten   #=> [3,4,5]
[1,2].concat(arr)  #=> [1,2,3,4,5]

Lastly, you can use our corelib gem (https://github.com/corlewsolutions/corelib) which adds useful helpers to the Ruby core classes. In particular we have an Array#add_all method which will automatically flatten multidimensional arrays before executing the concat.

Community
  • 1
  • 1
WebDev
  • 1,097
  • 8
  • 6
  • 1
    You usually want immutability, so creating a new array is a better idea. – vasilakisfil Aug 25 '15 at 14:10
  • 10
    "You usually want immutability" is not accurate. In 20+ years of full time software development I've worked with all kinds of arrays and collections on a daily basis. Sometimes you modify an existing array in place. Sometimes you need to work with a new instance. – WebDev Oct 10 '15 at 05:30
45
a = ["some", "thing"]
b = ["another", "thing"]

To append b to a and store the result in a:

a.push(*b)

or

a += b

In either case, a becomes:

["some", "thing", "another", "thing"]

but in the former case, the elements of b are appended to the existing a array, and in the latter case the two arrays are concatenated together and the result is stored in a.

snibbets
  • 1,095
  • 10
  • 11
  • 4
    Note that `a.push(*b)` is not exactly the same as `a += b`. The former adds the new elements to the existing array; the latter creates a new array with all elements and assigns it to `a`. You can see the difference if you do something like `aa = a` to save the ref to `a` before either append method and then examine `aa` afterwards. In the former case, it changes with the new value of `a`, and in the latter it remains unchanged. – Dave Hartnoll Jan 28 '19 at 00:17
  • 2
    NOTE: what @DaveHartnoll points out is *extremely important* for `each_with_object` usage and the like. Doing `each_with_object([]) { |thing, result| result += [thing] }` will not work, while using the `push` method does. – ragurney May 04 '21 at 01:20
43

Easy method that works with Ruby version >= 2.0 but not with older versions :

irb(main):001:0> a=[1,2]
=> [1, 2]
irb(main):003:0> b=[3,4]
=> [3, 4]
irb(main):002:0> c=[5,6]
=> [5, 6]
irb(main):004:0> [*a,*b,*c]
=> [1, 2, 3, 4, 5, 6]
Ludovic Kuty
  • 4,868
  • 3
  • 28
  • 42
  • 3
    @Ikuty This is by far the most elegant solution I found, can you please explain whats happening with `*` here? – Abhinay Aug 13 '16 at 07:43
  • @Abhinay the plat operator explodes the array into elements thus creating a single-dimension array in the last line. – Omar Ali Oct 25 '16 at 20:39
  • `[*a, *b]` fails for older versions of ruby, ie, 1.8.7. And as much as Ruby wants to tell you its out of life, RHEL6 is still maintained, making Ruby 1.8 very much a significant target version. – Otheus Mar 24 '17 at 10:51
  • 3
    I don't think that justifies the -1 this answer gets. No ruby version mentioned by OP, ruby version explicitly mentioned in the answer, so... you want to be backward compatible with version pre alpha 0.0.0.0.1 ? This is one of the good solutions, depending on the ruby version – Ludovic Kuty Mar 26 '17 at 08:55
  • 4
    Just to point that this answer is very 'similar' to the very idiomatic JavaScript ES6 in which you could do `[...array1, ...array2]`, just remembering that the `splat` operator in ruby would be `*` instead of `...`. It makes it easier to remember – sandre89 Dec 13 '18 at 22:22
41

Here are two ways, notice in this case that the first way assigns a new array ( translates to somearray = somearray + anotherarray )

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray += anotherarray # => ["some", "thing", "another", "thing"]

somearray = ["some", "thing"]
somearray.concat anotherarray # => ["some", "thing", "another", "thing"]
EliadL
  • 6,230
  • 2
  • 26
  • 43
Joshua Cheek
  • 30,436
  • 16
  • 74
  • 83
37

Try this, it will combine your arrays removing duplicates

array1 = ["foo", "bar"]
array2 = ["foo1", "bar1"]

array3 = array1|array2

http://www.ruby-doc.org/core/classes/Array.html

Further documentation look at "Set Union"

g00se0ne
  • 4,560
  • 2
  • 21
  • 14
  • 1
    This is an or, it returns an array with no duplicate elements, here is an example of how it probably doesn't do what he is asking, the two "baz" in the first array get turned into one, and the "bar" in the second array doesn't get added. array1 = ["foo", "bar" , "baz" , "baz" ] array2 = ["foo1", "bar1" , "bar" ] array3 = array1|array2 array3 # => ["foo", "bar", "baz", "foo1", "bar1"] – Joshua Cheek Nov 26 '09 at 04:39
  • 1
    Or even better: `array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]` – Joshua Pinter Apr 14 '18 at 04:15
22
(array1 + array2).uniq

This way you get array1 elements first. You will get no duplicates.

Deepak Mahakale
  • 22,834
  • 10
  • 68
  • 88
slindsey3000
  • 4,053
  • 5
  • 36
  • 56
13
["some", "thing"] + ["another", "thing"]
Deepak Mahakale
  • 22,834
  • 10
  • 68
  • 88
samg
  • 3,496
  • 1
  • 25
  • 26
13

Elaborating on @Pilcrow's answer the only suitable answer for huge arrays is concat (+) since is fast and does not allocate a new object to be garbage-collected when operating inside a loop.

Here's the benchmark:

require 'benchmark'

huge_ary_1 = Array.new(1_000_000) { rand(5_000_000..30_000_00) }

huge_ary_2 = Array.new(1_000_000) { rand(35_000_000..55_000_00) }

Benchmark.bm do |bm|
  p '-------------------CONCAT ----------------'
  bm.report { huge_ary_1.concat(huge_ary_2) }

  p '------------------- PUSH ----------------'
  bm.report { huge_ary_1.push(*huge_ary_2)  }
end

Results:

       user     system      total        real
"-------------------CONCAT ----------------"
  0.000000   0.000000   0.000000 (  0.009388)
"------------------- PUSH ----------------"
  example/array_concat_vs_push.rb:13:in `block (2 levels) in <main>': stack level too deep (SystemStackError)

As you can see using push throws an ERROR: stack level too deep (SystemStackError) when the arrays are big enough.

juliangonzalez
  • 4,231
  • 2
  • 37
  • 47
10

Just another way of doing it.

[somearray, anotherarray].flatten
=> ["some", "thing", "another", "thing"]
Datt
  • 851
  • 9
  • 21
  • `flatten` flattens everything as far as possible, recursively. Even nested arrays. Consequently, if `somearray` or `anotherarray` contains nested arrays, they get flattened, too. This is a side-effect that is usually not intended. – hagello May 01 '20 at 16:10
8

The question, essentially, is "how to concatenate arrays in Ruby". Naturally the answer is to use concat or + as mentioned in nearly every answer.

A natural extension to the question would be "how to perform row-wise concatenation of 2D arrays in Ruby". When I googled "ruby concatenate matrices", this SO question was the top result so I thought I would leave my answer to that (unasked but related) question here for posterity.


In some applications you might want to "concatenate" two 2D arrays row-wise. Something like,

[[a, b], | [[x],    [[a, b, x],
 [c, d]] |  [y]] =>  [c, d, y]]

This is something like "augmenting" a matrix. For example, I used this technique to create a single adjacency matrix to represent a graph out of a bunch of smaller matrices. Without this technique I would have had to iterate over the components in a way that could have been error prone or frustrating to think about. I might have had to do an each_with_index, for example. Instead I combined zip and flatten as follows,

# given two multi-dimensional arrays that you want to concatenate row-wise
m1 = [[:a, :b], [:c, :d]]
m2 = [[:x], [:y]]

m1m2 = m1.zip(m2).map(&:flatten)
# => [[:a, :b, :x], [:c, :d, :y]]
Ziggy
  • 21,845
  • 28
  • 75
  • 104
6
somearray = ["some", "thing"]
anotherarray = ["another", "thing"]
somearray + anotherarray # => ["some", "thing", "another", "thing"]
somearray.concat anotherarray # => ["some", "thing", "another", "thing"]
somearray.push(anotherarray).flatten # => ["some", "thing", "another", "thing"]
somearray.push *anotherarray # => ["another", "thing", "another", "thing"]
 
5

If the new data could be an array or a scalar, and you want to prevent the new data to be nested if it was an array, the splat operator is awesome! It returns a scalar for a scalar, and an unpacked list of arguments for an array.

1.9.3-p551 :020 > a = [1, 2]
 => [1, 2] 
1.9.3-p551 :021 > b = [3, 4]
 => [3, 4] 
1.9.3-p551 :022 > c = 5
 => 5 
1.9.3-p551 :023 > a.object_id
 => 6617020 
1.9.3-p551 :024 > a.push *b
 => [1, 2, 3, 4] 
1.9.3-p551 :025 > a.object_id
 => 6617020 
1.9.3-p551 :026 > a.push *c
 => [1, 2, 3, 4, 5] 
1.9.3-p551 :027 > a.object_id
 => 6617020 
Sandip Bhattacharya
  • 1,042
  • 10
  • 11
5

I'm surprised nobody has mentioned reduce, which works well when you have an array of arrays:

lists = [["a", "b"], ["c", "d"]]
flatlist = lists.reduce(:+)  # ["a", "b", "c", "d"]
ScottJ
  • 1,080
  • 12
  • 20
5
a = ['a', 'b']
b = ['c', 'd']
arr = [a, b].flatten

This won't remove dups, but

a|b

removes dups.

AustintheCleric
  • 349
  • 3
  • 15
4

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray + anotherarray

3

I find it easier to push or append arrays and then flatten them in place, like so:

somearray = ["some", "thing"]
anotherarray = ["another", "thing"]
somearray.push anotherarray # => ["some", "thing", ["another", "thing"]]
#or
somearray << anotherarray # => ["some", "thing", ["another", "thing"]]
somearray.flatten!  # => ["some", "thing", "another", "thing"]
somearray # => ["some", "thing", "another", "thing"]
nas
  • 3,676
  • 19
  • 19