514
votes
somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

I expected

["some","thing","another","thing"]
17
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
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
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
@yeyo, don't you just think that flatten operation is free?Konstantin
@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

17 Answers

762
votes

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
214
votes

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

74
votes

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.

38
votes

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]
38
votes

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"]
38
votes
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.

35
votes

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"

21
votes

(array1 + array2).uniq

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

11
votes

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.

9
votes

Just another way of doing it.

[somearray, anotherarray].flatten
=> ["some", "thing", "another", "thing"]
8
votes

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]]
8
votes

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

5
votes

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 
5
votes
a = ['a', 'b']
b = ['c', 'd']
arr = [a, b].flatten

This won't remove dups, but

a|b

removes dups.

4
votes

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"]
4
votes

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray + anotherarray

3
votes

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"]