591

I tried:

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

I expected

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

but got

["some", "thing", nil]
6
  • 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. Commented Nov 26, 2009 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.) Commented Jan 31, 2016 at 15:39
  • 16
    Questions get upvotes if they are useful to users. The simplest questions get the most upvotes because they are useful to the most people. Commented Jan 9, 2018 at 3:09
  • @yeyo, don't you just think that flatten operation is free? Commented Feb 8, 2018 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. Commented Feb 9, 2018 at 13:41

18 Answers 18

839

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
Sign up to request clarification or add additional context in comments.

14 Comments

Using push instead of concat avoids the creation of a third array, so this is preferred for large arrays.
+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.
@phatmann Concatenation with Array#concat does not allocate a new array, Concatenation with Array#+ does
@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.
The only thing this answer is missing is benchmark comparisons of each approach. +1!
|
236

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

7 Comments

The poster wanted to know how to concat to an existing array, not create a new array that was the union of two arrays.
Note: a+= b creates a new array: c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
@kbrock Correct. If dealing with large arrays, you'll want to look at the push method as described by @pilcrow.
remember that += creates new object. in such example [1, 2].each_with_object([]) { |number, object| object+=number } empty array [] will be returned
Item added must be an array
|
90

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.

2 Comments

You usually want immutability, so creating a new array is a better idea.
"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.
47
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.

2 Comments

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

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]

5 Comments

@Ikuty This is by far the most elegant solution I found, can you please explain whats happening with * here?
@Abhinay the plat operator explodes the array into elements thus creating a single-dimension array in the last line.
[*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.
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
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
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"]

Comments

38

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"

2 Comments

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"]
Or even better: array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
23
(array1 + array2).uniq

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

Comments

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

2 Comments

I don't know about efficiency, but this works for Ruby 1.8. In general, [*a] + [*b] works
I don't think that "another" + "thing" is going to work as expected.
14

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.

Comments

10

Just another way of doing it.

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

1 Comment

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

Comments

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

Comments

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 

Comments

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

Comments

5
a = ['a', 'b']
b = ['c', 'd']
arr = [a, b].flatten

This won't remove dups, but

a|b

removes dups.

1 Comment

Note: This recursively flattens all inner arrays as well.
4

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray + anotherarray

Comments

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.