110

I have the following code:

a = ["Cat", "Dog", "Mouse"]
s = ["and", "&"]

I want to merge the array s into array a which would give me:

["Cat", "and", "Dog", "&", "Mouse"]

Looking through the Ruby Array and Enumerable docs, I don't see such a method that will accomplish this.

Is there a way I can do this without iterating through each array?

1
  • 1
    a will always have 3 elements and s two? some more examples would be useful. Commented Sep 5, 2011 at 21:55

13 Answers 13

174

You can do that with:

a.zip(s).flatten.compact
Sign up to request clarification or add additional context in comments.

11 Comments

What if a has more than 3 elements?
[ "a", "b" ].concat( ["c", "d"] ) #=> [ "a", "b", "c", "d" ]
@Leo, @chuck: if you read the example you will see that Chris wants to interleave the elements, not concatenate them. Essentially, he wants [a, s].transpose except that the two rows don't conform, leaving #zip as the obvious solution. And I don't believe he meant that he really cared whether a was mutated ... I don't think he was commenting on a mutated vs functional solution at all, he was just trying to describe the pattern.
+1 for being the only person who actually read the blummin' question! >_<
More importantly, what if the two arrays are of unequal lengths? Especially if s is the longer one? I think I can safely assume the example Chris gave isn't he actual data he;s working with. consider: [].zip[1, 2] => nil (going to have a hard time calling #flatten on that) [3,4].zip([1, 3, 5, 7]) => [[3, 1], [4, 3]] (oops, guess we don't care about the last few elements in the 2nd array)
|
34

This won't give a result array in the order Chris asked for, but if the order of the resulting array doesn't matter, you can just use a |= b. If you don't want to mutate a, you can write a | b and assign the result to a variable.

See the set union documentation for the Array class at http://www.ruby-doc.org/core/classes/Array.html#M000275.

This answer assumes that you don't want duplicate array elements. If you want to allow duplicate elements in your final array, a += b should do the trick. Again, if you don't want to mutate a, use a + b and assign the result to a variable.

In response to some of the comments on this page, these two solutions will work with arrays of any size.

3 Comments

This one definitely seems to be the best.
This gives ["Cat", "Dog", "Mouse", "and", "&"], which isn't what the OP wanted.
Excellent call, Andrew. I'll update my answer to say that I didn't answer Chris's question.
32

If you don't want duplicate, why not just use the union operator :

new_array = a | s

4 Comments

Of course it answers the question ! The question was : "I want to merge the array s into array a"
Good solution - but this does change the order of the results. The results from s will be at the end of the new array.
The order of the elements won't be what the OP wanted, though.
most compact answer. If ordering were necessary, I would consider that a second step.
7

To handle the situation where both a & s are not of the same size:

a.zip(s).flatten.compact | s
  • .compact will remove nil when a is larger than s
  • | s will add the remaining items from s when a is smaller than s

Comments

6
s.inject(a, :<<)

s   #=> ["and", "&"]
a   #=> ["Cat", "Dog", "Mouse", "and", "&"]

It doesn't give you the order you asked for, but it's a nice way of merging two arrays by appending to the one.

2 Comments

I like it, short and clean. :)
well not as short and clean as a | s
6

Here's a solution that allows interleaving multiple arrays of different sizes (general solution):

arr = [["Cat", "Dog", "Mouse", "boo", "zoo"],
 ["and", "&"],
 ["hello", "there", "you"]]

first, *rest = *arr; first.zip(*rest).flatten.compact
=> ["Cat", "and", "hello", "Dog", "&", "there", "Mouse", "you", "boo", "zoo"]

2 Comments

Nice! One limitation, the first array must be the longest.
@BrianLow great catch!
5

It's not exactly elegant, but it works for arrays of any size:

>> a.map.with_index { |x, i| [x, i == a.size - 2 ? s.last : s.first] }.flatten[0..-2] 
#=> ["Cat", "and", "Dog", "&", "Mouse"]

2 Comments

+1 for dealing with weird edge cases, I think i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2] would be equally valid though.
I wasn't sure if OP wants to alternate and and &, so I took him as literally as possible, while allowing for a of any length.
3

How about a more general solution that works even if the first array isn't the longest and accepts any number of arrays?

a = [
    ["and", "&"],
    ["Cat", "Dog", "Mouse"]
]

b = a.max_by(&:length)
a -= [b]
b.zip(*a).flatten.compact

 => ["Cat", "and", "Dog", "&", "Mouse"]

Comments

1

One way to do the interleave and also guarantee which one is the biggest array for the zip method, is to fill up one of the arrays with nil until the other array size. This way, you also guarantee which element of which array will be on first position:

preferred_arr = ["Cat", "Dog", "Mouse"]
other_arr = ["and","&","are","great","friends"]

preferred_arr << nil while preferred_arr.length < other_arr.length
preferred_arr.zip(other_arr).flatten.compact
#=> ["Cat", "and", "Dog", "&", "Mouse", "are", "great", "friends"]

1 Comment

Bit better: preferred_arr.zip(other_arr).flatten | other_arr (without the nil backfilling)
0

Interleave 2D array of any size

arr = [["Cat", "Dog", "Mouse"],
 ["and", "&"],
 ["hello", "there", "you", "boo", "zoo"]]

max_count = arr.map(&:count).max
max_count.times.map{|i| arr.map{|a| a[i]}}.flatten.compact

#=> ["Cat", "and", "hello", "Dog", "&", "there", "Mouse", "you", "boo", "zoo"]

Comments

0

A very clear way to merge multiple arrays is to unpack them into one array. This works in practically the same way for many languages, so I'd prefer this method due to its simplicity and developer familiarity with it.

a = ["Cat", "Dog", "Mouse"]
s = ["and", "&"]

[*a, *s]
#=> ["Cat", "Dog", "Mouse", "and", "&"]

Comments

0
def merge_and_interleave(arr_a, arr_b)
  final_arr = [] 

  until arr_a.empty? && arr_b.empty?
    final_arr << arr_a.shift unless arr_a.empty?
    final_arr << arr_b.shift unless arr_b.empty? 
  end

  final_arr
end

1 Comment

This empties the arrays passed without even documenting that.
-2
arr = [0, 1]
arr + [2, 3, 4]

//outputs [0, 1, 2, 3, 4]

1 Comment

sorry... didn't notice the specific order you wanted the output in. Apologies for trying to help, wont happen again.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.