Rubyで2つの配列をマージしてインターリーブする


106

私は次のコードを持っています:

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

私は配列sを配列にマージしたいaと思います:

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

Ruby ArrayとEnumerableのドキュメントを見ると、これを実現するようなメソッドは見当たりません。

各配列を繰り返さずにこれを行う方法はありますか?


aには常に3つの要素があり、sには2つの要素がありますか?さらにいくつかの例が役に立つでしょう。
tokland

回答:


171

あなたはそれを行うことができます:

a.zip(s).flatten.compact

4
a3つ以上の要素がある場合はどうなりますか?
マイケルコール、2011

116
["a"、 "b"] .concat(["c"、 "d"])#=> ["a"、 "b"、 "c"、 "d"]
Leo Romanovsky

30
@ Leo、@ chuck:この例を読むと、Chris は要素を連結するのではなく、要素をインターリーブしたいことがわかります。基本的に、彼は2つの行が適合しないことを除いて望んでおり、明白な解決策として残しています。そして、彼が変異したかどうかを本当に気にかけていたとは思わない...変異したvs機能的なソリューションについてはまったくコメントしていなかったと思う。彼はパターンを説明しようとしただけだ。[a, s].transpose#zipa
DigitalRoss

15
ブラムミンの質問を実際に読んだ唯一の人物であることの+1!> _ <
マットフレッチャー

5
さらに重要なことに、2つの配列の長さが等しくない場合はどうなりますか?特にsが長い場合はどうでしょうか?クリスが与えた例は、彼が実際に使用しているデータではないことは間違いないと思います。検討してください:[] .zip [1、2] => nil(#flattenを呼び出すのに苦労します)[3,4] .zip([1、3、5、7])=> [[3 、1]、[
4、3

32

これは、Chrisが要求した順序で結果の配列を提供しませんが、結果の配列の順序が重要でない場合は、を使用できますa |= b。変更したくない場合は、結果をa書き込んa | bで変数に割り当てることができます。

http://www.ruby-doc.org/core/classes/Array.html#M000275にあるArrayクラスのセットユニオンドキュメントを参照してください

この回答は、重複する配列要素が不要であることを前提としています。最終的な配列で要素の重複を許可する場合a += bは、トリックを実行する必要があります。繰り返しますが、mutateしたくない場合は、結果をa使用a + bして変数に割り当てます。

このページのコメントのいくつかに応えて、これら2つのソリューションは任意のサイズの配列で機能します。


これは間違いなく最高のようです。
ardavis、2011

11
これはを与えるが["Cat", "Dog", "Mouse", "and", "&"]、これはOPが望んでいたものではない。
Andrew Grimm

素晴らしいコール、アンドリュー。クリスの質問に答えなかったと答えるように、答えを更新します。
Michael Stalker

29

複製したくない場合は、ユニオン演算子を使用しないでください。

new_array = a | s

1
過小評価されたシンプルでエレガントなソリューションに対して+1を授与します。
Giacomo1968 2015年

もちろんそれは質問に答えます!問題は、「配列sを配列aにマージしたい」
ダグラス

良い解決策-しかし、これは結果の順序を変更します。からの結果sは、新しい配列の最後になります。
Hendrik

1
ただし、要素の順序はOPが望んだものとは異なります。
tokland 2016

6
s.inject(a, :<<)

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

それはあなたが要求した順序を与えませんが、1つに追加することによって2つの配列をマージする良い方法です。


短くてきれいです。:)
Nafaa Boutefer、2015年

6

これは、異なるサイズの複数の配列をインターリーブできるソリューションです(一般的なソリューション)。

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
いいね!1つの制限として、最初の配列は最も長くなければなりません。
ブライアンロー

@BrianLow大漁!
Abdo

5

エレガントではありませんが、任意のサイズの配列で機能します。

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

奇妙なエッジケースを処理するための+1も、私i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2]は同様に有効だと思います。
muが短すぎる

私は確信しOPは、代替しようとする場合ではなかったand&を可能にしながら、私は、文字通りできるだけ彼を連れて行ったので、a任意の長さの。
マイケルコール、2011

3

最初の配列が最も長くなく、任意の数の配列を受け入れる場合でも機能する、より一般的なソリューションはどうでしょうか?

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

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

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

2

a&のsサイズが同じではない状況に対処するには:

a.zip(s).flatten.compact | s
  • .compactより大きい場合に削除さnilaますs
  • | sがより小さいsときの残りのアイテムを追加しますas

1

インターリーブを実行し、zipメソッドの最大の配列を保証する1つの方法は、配列の1つをnil他の配列サイズまで埋めることです。このようにして、どの配列のどの要素が最初の位置になるかを保証します。

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
少し良い:preferred_arr.zip(other_arr).flatten | other_arr(nilバックフィルなし)
Adam Fendley '15

-2
arr = [0, 1]
arr + [2, 3, 4]

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

5
申し訳ありませんが...あなたが出力したい特定の順序に気づきませんでした。助けようとしたことの謝罪、二度と起こりません。
David Morrow
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.