Bagaimana Anda menambahkan array ke array lain di Ruby dan tidak berakhir dengan hasil multi-dimensi?

474
somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

saya mengharapkan

["some","thing","another","thing"]
ncvncvn
sumber
6
Layak dikatakan (bukan untuk memberi Anda kesedihan, tetapi karena itu akan menggigit Anda lagi dan lagi) bahwa harapan Anda adalah masalah di sini. Array Ruby (tidak seperti say array di Perl) tidak secara otomatis rata dalam konteks seperti ini. Ini bukan bug: ini fitur.
Telemachus
3
ri Array@flatten!Mengapa pertanyaan ini mendapatkan begitu banyak suara? Doc secara eksplisit Array#flatten! Ratakan diri di tempat. Mengembalikan nil jika tidak ada modifikasi yang dilakukan (yaitu, array tidak mengandung subarrays.)
yeyo
7
Pertanyaan mendapatkan suara jika berguna bagi pengguna. Pertanyaan-pertanyaan paling sederhana mendapatkan paling banyak upvotes karena mereka berguna bagi kebanyakan orang.
Ziggy
@Yeyo, bukankah kamu hanya berpikir bahwa operasi rata gratis?
Konstantin
@Konstantin op tidak mencari alternatif atau berbicara tentang masalah kinerja, op mengharapkan hasil yang tidak ia dapatkan karena flatten!tidak berfungsi seperti itu. Akhirnya, pertanyaan tersebut mencerminkan masalah logika daripada masalah optimisasi. Lihat jawaban pilcrow di bawah ini untuk informasi lebih lanjut.
yeyo

Jawaban:

713

Anda punya ide yang bisa diterapkan, tetapi #flatten!ada di tempat yang salah - itu meratakan penerima, sehingga Anda bisa menggunakannya untuk berubah [1, 2, ['foo', 'bar']]menjadi [1,2,'foo','bar'].

Saya pasti lupa beberapa pendekatan, tetapi Anda dapat menggabungkan :

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

atau tambahkan / tambahkan :

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

atau sambatan :

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

atau tambahkan dan ratakan :

(a1 << a2).flatten!  # a call to #flatten instead would return a new array
pilcrow
sumber
17
dilakukan dengan baik karena menjadi satu-satunya (dari 5 saya dapat melihat) yang benar-benar menunjukkan apa yang salah dengan kode yang disajikan. +1
Mike Woodhouse
53
Menggunakan push daripada concat menghindari pembuatan array ketiga, jadi ini lebih disukai untuk array besar.
phatmann
8
Saya suka dorongan dengan tanda bintang. Sangat elegan.
orourkedd
14
@phatmann Concatenation with Array#concattidak mengalokasikan array baru, Concatenation with Array#+does
cbliard
5
Satu-satunya hal yang hilang dari jawaban ini adalah perbandingan tolok ukur dari setiap pendekatan. +1!
Terra Ashley
206

Anda cukup menggunakan +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]

Anda dapat membaca semua tentang kelas array di sini: http://ruby-doc.org/core/classes/Array.html

micmoo
sumber
15
Poster ingin tahu bagaimana cara menggabungkan ke array yang ada, bukan membuat array baru yang merupakan gabungan dari dua array.
phatmann
1
Catatan: a+= bmembuat larik baru:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
kbrock
1
@ kbrock Benar. Jika berurusan dengan array besar , Anda harus melihat pushmetode seperti yang dijelaskan oleh @pilcrow.
Joshua Pinter
2
ingat yang +=menciptakan objek baru. dalam contoh seperti [1, 2].each_with_object([]) { |number, object| object+=number }array kosong []akan dikembalikan
Filip Bartuzi
1
Item yang ditambahkan harus berupa array
RousseauAlexandre
66

Pendekatan terbersih adalah dengan menggunakan metode # array concat ; itu tidak akan membuat array baru (tidak seperti Array # + yang akan melakukan hal yang sama tetapi membuat array baru).

Langsung dari dokumen ( http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat ):

concat (other_ary)

Menambahkan elemen other_ary ke diri sendiri.

Begitu

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

Array # concat tidak akan meratakan array multidimensi jika dilewatkan sebagai argumen. Anda harus menanganinya secara terpisah:

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

Terakhir, Anda dapat menggunakan permata corelib kami ( https://github.com/corlewsolutions/corelib ) yang menambahkan pembantu yang berguna ke kelas inti Ruby. Secara khusus kami memiliki metode # add_all Array yang akan secara otomatis meratakan array multidimensi sebelum menjalankan concat.

Solusi Corlew
sumber
1
Anda biasanya menginginkan kekekalan, jadi membuat array baru adalah ide yang lebih baik.
vasilakisfil
5
"Anda biasanya menginginkan kekekalan" tidak akurat. Dalam 20+ tahun pengembangan perangkat lunak penuh waktu, saya telah bekerja dengan semua jenis array dan koleksi setiap hari. Terkadang Anda memodifikasi array yang ada di tempat. Terkadang Anda perlu bekerja dengan instance baru.
Solusi Corlew
35

Metode mudah yang berfungsi dengan versi Ruby> = 2.0 tetapi tidak dengan versi yang lebih lama:

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
sumber
2
@Ikuty Sejauh ini ini adalah solusi paling elegan yang saya temukan, bisakah Anda menjelaskan apa yang terjadi dengan *sini?
Abhinay
@Abhinay operator plat meledak array menjadi elemen sehingga menciptakan array dimensi tunggal di baris terakhir.
Omar Ali
[*a, *b]gagal untuk ruby ​​versi lama, yaitu 1.8.7. Dan sebanyak yang ingin disampaikan Ruby kepada Anda tentang kehidupannya, RHEL6 tetap dipertahankan, menjadikan Ruby 1.8 sebagai versi target yang sangat signifikan.
Otheus
1
Saya tidak berpikir bahwa membenarkan -1 jawaban ini didapat. Tidak ada versi ruby ​​yang disebutkan oleh OP, versi ruby ​​yang disebutkan secara eksplisit dalam jawabannya, jadi ... Anda ingin kompatibel dengan versi pre alpha 0.0.0.0.1? Ini adalah salah satu solusi yang baik, tergantung pada versi ruby
Ludovic Kuty
1
Hanya untuk menunjukkan bahwa jawaban ini sangat 'mirip' dengan JavaScript ES6 yang sangat idiomatis di mana Anda dapat melakukannya [...array1, ...array2], hanya mengingat bahwa splatoperator di ruby ​​akan menjadi *pengganti .... Itu membuatnya lebih mudah untuk diingat
sandre89
34

Coba ini, ini akan menggabungkan array Anda menghapus duplikat

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

array3 = array1|array2

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

Dokumentasi lebih lanjut lihat "Set Union"

g00se0ne
sumber
Ini adalah atau, ia mengembalikan array tanpa elemen duplikat, di sini adalah contoh bagaimana itu mungkin tidak melakukan apa yang dia minta, dua "baz" di array pertama bisa berubah menjadi satu, dan "bar" dalam array kedua tidak ditambahkan. array1 = ["foo", "bar", "baz", "baz"] array2 = ["foo1", "bar1", "bar"] array3 = array1 | array2 array3 # => ["foo", "bar "," baz "," foo1 "," bar1 "]
Joshua Cheek
Atau bahkan lebih baik:array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
Joshua Pinter
33

Berikut adalah dua cara, perhatikan dalam hal ini bahwa cara pertama menetapkan array baru (diterjemahkan ke 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"]
Joshua Cheek
sumber
25
a = ["some", "thing"]
b = ["another", "thing"]

Untuk menambahkan bke adan menyimpan hasilnya dalam a:

a.push(*b)

atau

a += b

Dalam kedua kasus, amenjadi:

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

tetapi dalam kasus sebelumnya, unsur-unsur bditambahkan ke aarray yang ada , dan dalam kasus yang kedua dua array digabungkan bersama-sama dan hasilnya disimpan di a.

snibbets
sumber
2
Perhatikan bahwa a.push(*b)tidak persis sama dengan a += b. Yang pertama menambahkan elemen baru ke array yang ada; yang terakhir membuat array baru dengan semua elemen dan menugaskannya a. Anda dapat melihat perbedaannya jika Anda melakukan sesuatu seperti aa = amenyimpan ref asebelum sebelum menambahkan metode dan kemudian memeriksanya aa. Dalam kasus yang pertama, itu berubah dengan nilai baru a, dan yang terakhir itu tetap tidak berubah.
Dave Hartnoll
20

(array1 + array2).uniq

Dengan cara ini Anda mendapatkan elemen array1 terlebih dahulu. Anda tidak akan mendapatkan duplikat.

slindsey3000
sumber
9

Menguraikan jawaban @ Pilcrow satu-satunya jawaban yang cocok untuk array besar adalah concat(+ ) karena cepat dan tidak mengalokasikan objek baru untuk pengumpulan sampah saat beroperasi di dalam sebuah loop.

Inilah patokannya:

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

Hasil:

       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)

Seperti yang Anda lihat menggunakan pushthrows ERROR : stack level too deep (SystemStackError)ketika array cukup besar.

juliangonzalez
sumber
8

Pertanyaannya, pada dasarnya, adalah "bagaimana menggabungkan array di Ruby". Secara alami jawabannya adalah menggunakan concatatau+ seperti yang disebutkan dalam hampir setiap jawaban.

Sebuah ekstensi alami untuk pertanyaan adalah "bagaimana melakukan penggabungan baris 2D dari array 2D di Ruby". Ketika saya googled "ruby concatenate matrices", pertanyaan SO ini adalah hasil teratas jadi saya pikir saya akan meninggalkan jawaban saya untuk pertanyaan (tanpa diminta tetapi terkait) di sini untuk anak cucu.


Dalam beberapa aplikasi Anda mungkin ingin "menggabungkan" dua array 2D bijaksana. Sesuatu seperti,

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

Ini adalah sesuatu seperti "menambah" sebuah matriks. Sebagai contoh, saya menggunakan teknik ini untuk membuat matriks adjacency tunggal untuk mewakili grafik dari sekelompok matriks yang lebih kecil. Tanpa teknik ini saya harus beralih pada komponen dengan cara yang bisa menjadi rawan kesalahan atau frustasi untuk dipikirkan. Saya mungkin harus melakukan each_with_index, misalnya. Sebagai gantinya saya menggabungkan zip dan meratakan sebagai berikut,

# 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
sumber
8

Hanya cara lain untuk melakukannya.

[somearray, anotherarray].flatten
=> ["some", "thing", "another", "thing"]
Datt
sumber
flattenmeratakan segalanya sejauh mungkin, secara rekursif. Bahkan array bersarang. Akibatnya, jika somearrayatau anotherarraymengandung array bersarang, mereka juga diratakan. Ini adalah efek samping yang biasanya tidak dimaksudkan.
hagello
5

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

hai
sumber
Saya tidak tahu tentang efisiensi, tetapi ini berfungsi untuk Ruby 1.8. Secara umum, [*a] + [*b]bekerja
Otheus
Saya tidak berpikir itu "another" + "thing"akan berfungsi seperti yang diharapkan.
Alexis Wilke
5

Jika data baru bisa berupa array atau skalar, dan Anda ingin mencegah data baru untuk bersarang jika itu adalah array, operator percikan mengagumkan! Ini mengembalikan skalar untuk skalar, dan daftar argumen untuk 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
sumber
4

Saya terkejut tidak ada yang disebutkan reduce, yang bekerja dengan baik ketika Anda memiliki array array:

lists = [["a", "b"], ["c", "d"]]
flatlist = lists.reduce(:+)  # ["a", "b", "c", "d"]
ScottJ
sumber
4
a = ['a', 'b']
b = ['c', 'd']
arr = [a, b].flatten

Ini tidak akan menghapus dups, tetapi

a|b

menghapus dups.

AustintheCleric
sumber
Catatan: Ini juga secara mendatar meratakan semua array dalam juga.
Mirodinho
2

Saya merasa lebih mudah untuk mendorong atau menambahkan array dan kemudian meratakannya di tempat, seperti:

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
sumber
2

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray + anotherarray

Lyle Dickie
sumber