Ruby'de başka bir diziye bir dizi nasıl eklersiniz ve çok boyutlu bir sonuç elde edemezsiniz?


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

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

Tahmin etmiştim

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

6
Sizi keder vermemek, ama sizi tekrar tekrar ısıtacağından, beklentinizin sorun olduğunu söylemeye değer. Yakut diziler (Perl aksine diyelim diziler) do not otomatik olarak böyle bağlamlarda dümdüz. Bu bir hata değil: bir özellik.
Telemachus

3
ri Array@flatten!Bu soru neden bu kadar çok oy alıyor? Doktor açıkça Array#flatten! Flattens kendini yerinde. Hiçbir değişiklik yapılmadıysa nil değerini döndürür (yani, dizi alt diziler içermez.)
yeyo

7
Sorular, kullanıcılar için yararlı olup olmadıklarını gösterir. En basit sorular en çok oyu alır çünkü çoğu insan için faydalıdır.
Ziggy

@yeyo, sadece düzleştirme işleminin ücretsiz olduğunu düşünmüyor musunuz?
Konstantin

@Konstantin op alternatifler aramıyor veya performans sorunları hakkında konuşmuyor, op elde edemediği bir sonuç bekliyordu çünkü flatten!böyle çalışmıyor. Son olarak, soru bir optimizasyon probleminden ziyade bir mantık problemini yansıtmaktadır. Daha fazla bilgi için aşağıdaki pilcrow cevabına bakın.
yeyo

Yanıtlar:


713

Uygulanabilir bir fikriniz var, ancak #flatten!yanlış yerde - alıcısını düzleştirir, böylece [1, 2, ['foo', 'bar']]dönüşmek için kullanabilirsiniz [1,2,'foo','bar'].

Şüphesiz bazı yaklaşımları unutuyorum, ancak birleştirebilirsiniz :

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

veya başa ekle / ekle :

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

veya ekleme :

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

veya ekleyin ve düzleştirin :

(a1 << a2).flatten!  # a call to #flatten instead would return a new array

17
gerçekten sunulan kod ile neyin yanlış olduğunu işaret tek (5 görebiliyorum) olmak için aferin. +1
Mike Woodhouse

53
Concat yerine push kullanmak üçüncü bir dizinin oluşturulmasını önler, bu nedenle bu büyük diziler için tercih edilir.
phatmann

8
Yıldız işareti ile itmeyi seviyorum. Çok zarif.
orourkedd

14
@phatmann ile Birleştirme Array#concatyeni bir dizi ayırmaz, birleştirme ile Array#+yapar
cbliard

5
Bu cevabın eksik olduğu tek şey, her bir yaklaşımın karşılaştırmalı karşılaştırmalarıdır. 1!
Terra Ashley

205

Sadece +operatörü kullanabilirsiniz !

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]

Dizi sınıfıyla ilgili her şeyi buradan okuyabilirsiniz: http://ruby-doc.org/core/classes/Array.html


15
Poster, iki dizinin birleşimi olan yeni bir dizi oluşturmak yerine mevcut bir diziye nasıl bağlanacağını bilmek istiyordu.
phatmann

1
Not: a+= byeni bir dizi oluşturur:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
kbrock

1
@kbrock Doğru. Büyük dizilerle uğraşıyorsanız, push@pilcrow tarafından açıklanan yönteme bakmak istersiniz.
Joshua Pinter

2
bunun +=yeni bir nesne yarattığını unutmayın . bu örnekte [1, 2].each_with_object([]) { |number, object| object+=number }boş dizi []döndürülecektir
Filip Bartuzi

1
Eklenen öğe bir dizi olmalıdır
RousseauAlexandre

66

En temiz yaklaşım Array # concat yöntemini kullanmaktır; yeni bir dizi oluşturmaz (Array # + 'dan farklı olarak aynı şeyi yapar ancak yeni bir dizi oluşturur).

Doğrudan dokümanlardan ( http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat ):

concat (other_ary)

Other_ary öğelerini kendine ekler.

Yani

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

Array # concat , argüman olarak aktarılırsa çok boyutlu bir diziyi düzleştirmez. Bunu ayrıca ele almanız gerekir:

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

Son olarak, Ruby çekirdek sınıflarına yararlı yardımcılar ekleyen corelib gemimizi ( https://github.com/corlewsolutions/corelib ) kullanabilirsiniz . Özellikle, çok boyutlu dizileri concat gerçekleştirmeden önce otomatik olarak düzleştirecek bir Array # add_all yöntemimiz var.


1
Genellikle değişmezlik istersiniz, bu nedenle yeni bir dizi oluşturmak daha iyi bir fikirdir.
vasilakisfil

5
"Genellikle değişmezlik istiyorsunuz" doğru değil. 20 yılı aşkın tam zamanlı yazılım geliştirmede her türlü dizi ve koleksiyonla günlük olarak çalıştım. Bazen mevcut bir diziyi yerinde değiştirirsiniz. Bazen yeni bir örnekle çalışmanız gerekir.
Corlew Solutions

35

Ruby sürüm> = 2.0 ile çalışan, ancak eski sürümlerle çalışmayan kolay yöntem:

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]

2
@Ikuty Bu, bulduğum en zarif çözüm, *burada neler olduğunu açıklayabilir misiniz ?
Abhinay

@Abhinay, plat operatörü diziyi elemanlara ayırır, böylece son satırda tek boyutlu bir dizi oluşturur.
Omar Ali

[*a, *b]Ruby'nin eski sürümleri için başarısız olur, yani 1.8.7. Ve Ruby size hayat dışı olduğunu söylemek istediği kadar, RHEL6 hala korunuyor ve Ruby 1.8'i çok önemli bir hedef sürüm haline getiriyor.
Otheus

1
Bu cevabın aldığı -1'i haklı bulmuyorum. OP tarafından belirtilen ruby ​​sürümü yok, ruby ​​sürümü yanıtta açıkça belirtildi, bu yüzden ... sürüm alfa 0.0.0.0.1 ile geriye dönük olarak uyumlu olmak ister misiniz? Bu yakut versiyonuna bağlı olarak iyi çözümlerden biridir
Ludovic Kuty

1
Sadece bu cevabın yapabileceğiniz deyimsel JavaScript ES6'ya çok 'benzer' olduğunu belirtmek için [...array1, ...array2], sadece yakuttaki splatoperatörün *yerine olacağını hatırlamak .... Hatırlamayı kolaylaştırır
sandre89

34

Bunu deneyin, yinelemeleri kaldırarak dizilerinizi birleştirir

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

array3 = array1|array2

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

Diğer belgeler "Set Union" a bakınız


Bu bir ya da, yinelenen öğeleri olmayan bir dizi döndürür, işte muhtemelen sorduğu şeyi nasıl yapmadığına bir örnek, ilk dizideki iki "baz" bire dönüşür ve "bar" ikinci diziye eklenmez. array1 = ["foo", "bar", "baz", "baz"] array2 = ["foo1", "bar1", "bar"] dizi3 = dizi1 | dizi2 dizi3 # => ["foo", "çubuğu "," baz "," foo1 "," bar1 "]
Joshua Cheek

Ya da daha iyisi:array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
Joshua Pinter

33

İşte iki yol, bu durumda ilk yolun yeni bir dizi atadığına dikkat edin (somearray = somearray + anotherarray anlamına gelir)

somearray = ["some", "thing"]

anotherarray = ["another", "thing"]

somearray += anotherarray # => ["some", "thing", "another", "thing"]

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

24
a = ["some", "thing"]
b = ["another", "thing"]

Eklemek için biçin ave sonucu depolamak a:

a.push(*b)

veya

a += b

Her iki durumda da, aolur:

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

ancak önceki durumda, öğelerinin öğeleri bmevcut adiziye eklenir ve ikinci durumda iki dizi birleştirilir ve sonuç içinde depolanır a.


2
Bunun a.push(*b)tam olarak aynı olmadığını unutmayın a += b. Birincisi, yeni diziyi mevcut diziye ekler; ikincisi tüm öğelerle yeni bir dizi oluşturur ve bu diziye atar a. aa = aRef ayöntemini herhangi bir ekleme yönteminden önce kaydetmek ve daha sonra incelemek gibi bir şey yaparsanız farkı görebilirsiniz aa. İlk durumda, yeni değeri ile değişir ave ikincisinde değişmeden kalır.
Dave Hartnoll

20

(array1 + array2).uniq

Bu şekilde önce array1 öğelerini alırsınız. Hiçbir kopya almazsınız.


9

@ Pilcrow'un cevabında, büyük diziler için tek uygun cevap concat(+ ) hızlıdır ve bir döngü içinde çalışırken çöp toplanacak yeni bir nesne ayırmaz.

Kıyaslama İşte:

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

Sonuçlar:

       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)

Gördüğünüz gibi, pushbir HATA atar : stack level too deep (SystemStackError)diziler yeterince büyük olduğunda.


8

Asıl soru "Ruby'de dizilerin nasıl birleştirileceği" sorusudur. Doğal olarak cevap kullanmak concatveya+ hemen hemen her cevapta belirtildiği gibidir.

Sorunun doğal bir uzantısı, “2B dizilerin Ruby'de satır bazında birleştirilmesinin nasıl gerçekleştirileceği” olacaktır. Ne zaman "yakut bitiştirmek matrisler" googled, bu SO soru en iyi sonuç oldu bu yüzden ben burada (sorulmamış ama ilgili) soruya gelecek kuşak için cevap bırakacağımı düşündüm.


Bazı uygulamalarda, iki 2D diziyi satır bazında "birleştirmek" isteyebilirsiniz. Gibi bir şey,

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

Bu bir matrisi "artırmak" gibidir. Örneğin, bu tekniği bir grup küçük matrisin grafiğini temsil eden tek bir bitişiklik matrisi oluşturmak için kullandım. Bu teknik olmasaydı, bileşenler üzerinde hataya eğilimli ya da düşünmek için sinir bozucu olabilecek bir şekilde yinelemeliydim. each_with_indexMesela bir yapmak zorunda kalabilirdim . Bunun yerine fermuar ve düzleştirmeyi aşağıdaki gibi birleştirdim ,

# 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

Bunu yapmanın başka bir yolu.

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

flattenher şeyi olabildiğince tekrar tekrar düzleştirir. İç içe diziler bile. Sonuç olarak, iç içe diziler varsa somearrayveya anotherarrayiçeriyorsa, düzleştirilirler. Bu genellikle amaçlanmayan bir yan etkidir.
hagello

5

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


Verimliliği bilmiyorum, ama bu Ruby 1.8 için işe yarıyor. Genel olarak, [*a] + [*b]eserler
Otheus

Bunun "another" + "thing"beklendiği gibi çalışacağını düşünmüyorum .
Alexis Wilke

5

Yeni veriler bir dizi veya skaler olabilirse ve yeni verilerin bir dizi olması durumunda iç içe geçmesini önlemek istiyorsanız, uyarıcı işleci harikadır! Bir skaler için bir skaler ve bir dizi için paketlenmemiş bir argüman listesi döndürür.

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 

4

Kimsenin bahsetmediğine şaşırdım reduce, bir dizi diziniz olduğunda iyi çalışıyor:

lists = [["a", "b"], ["c", "d"]]
flatlist = lists.reduce(:+)  # ["a", "b", "c", "d"]

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

Bu, dup'leri kaldırmaz, ancak

a|b

dups kaldırır.


Not: Bu, tüm iç dizileri tekrar tekrar düzleştirir.
Mirodinho

2

Dizileri itmeyi veya eklemeyi ve daha sonra bunları yerinde düzleştirmeyi daha kolay buluyorum:

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

2

somearray = ["bazı", "şey"]

anotherarray = ["başka bir", "şey"]

bir şey + anotherarray

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.