Jak dodać tablicę do innej tablicy w Ruby i nie dać wyniku wielowymiarowego?

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

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

oczekiwałem

["some","thing","another","thing"]
ncvncvn
źródło
6
Warto powiedzieć (nie po to, by cię żałować, ale ponieważ będzie cię gryźć raz po raz), że twoje oczekiwania są tutaj problemem. Tablice Ruby (w przeciwieństwie do tablic w Perlu) nie spłaszczają się automatycznie w takich kontekstach. To nie jest błąd: to funkcja.
Telemachus
3
ri Array@flatten!Dlaczego to pytanie dostaje tyle głosów? Dokument jest wyraźnie Array#flatten! spłaszczony na miejscu. Zwraca zero, jeśli nie wprowadzono żadnych modyfikacji (tj. Tablica nie zawiera żadnych podcieni.)
yeyo
7
Pytania otrzymują pozytywne opinie, jeśli są przydatne dla użytkowników. Najprostsze pytania mają najwięcej pozytywnych opinii, ponieważ są przydatne dla większości ludzi.
Ziggy,
@ yeyo, czy nie uważasz, że spłaszczanie jest bezpłatne?
Konstantin
@Konstantin op nie szuka alternatyw ani nie mówi o problemach z wydajnością, op spodziewał się rezultatu, którego on lub ona nie uzyska, ponieważ flatten!tak nie działa. Wreszcie pytanie odzwierciedla raczej problem logiczny niż problem optymalizacji. Zobacz odpowiedź pilcrowa poniżej.
yeyo

Odpowiedzi:

713

Masz realne pomysł, ale #flatten!jest w złym miejscu - to spłaszcza swój odbiornik, więc można go używać, aby włączyć [1, 2, ['foo', 'bar']]się [1,2,'foo','bar'].

Bez wątpienia zapominam o niektórych podejściach, ale można połączyć :

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

lub dołącz / dołącz :

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

lub splicować :

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

lub dołącz i spłaszcz :

(a1 << a2).flatten!  # a call to #flatten instead would return a new array
pilcrow
źródło
17
dobrze zrobione, ponieważ jestem jedynym (z 5, które widzę), który faktycznie wskazał, co jest nie tak z prezentowanym kodem. +1
Mike Woodhouse
53
Użycie push zamiast concat pozwala uniknąć utworzenia trzeciej tablicy, więc jest to preferowane w przypadku dużych tablic.
phatmann
8
Uwielbiam push z gwiazdką. Bardzo elegancko.
orourkedd
14
@phatmann Concatenation with Array#concatnie przydziela nowej tablicy, Concatenation with Array#+does
cbliard
5
Jedyne, czego brakuje w tej odpowiedzi, to porównania porównawcze każdego podejścia. +1!
Terra Ashley,
205

Możesz po prostu użyć +operatora!

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]

Wszystko o klasie macierzy możesz przeczytać tutaj: http://ruby-doc.org/core/classes/Array.html

micmoo
źródło
15
Plakat chciał wiedzieć, jak połączyć się z istniejącą tablicą, a nie tworzyć nową tablicę, która byłaby połączeniem dwóch tablic.
phatmann
1
Uwaga: a+= btworzy nową tablicę:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
kbrock
1
@kbrock Prawidłowo. Jeśli masz do czynienia z dużymi tablicami, powinieneś przyjrzeć się pushmetodzie opisanej przez @pilcrow.
Joshua Pinter,
2
pamiętaj, że +=tworzy nowy obiekt. w takim przykładzie zostanie zwrócona [1, 2].each_with_object([]) { |number, object| object+=number }pusta tablica[]
Filip Bartuzi
1
Dodany element musi być tablicą
RousseauAlexandre
66

Najczystszym podejściem jest użycie metody Array # concat ; nie utworzy nowej tablicy (w przeciwieństwie do Array # +, która zrobi to samo, ale utworzy nową tablicę).

Prosto z dokumentacji ( http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat ):

concat (other_ary)

Dołącza elementy other_ary do siebie.

Więc

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

Tablica # concat nie spłaszczy wielowymiarowej tablicy, jeśli zostanie przekazana jako argument. Musisz poradzić sobie z tym osobno:

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

Na koniec możesz użyć naszego klejnotu corelib ( https://github.com/corlewsolutions/corelib ), który dodaje użyteczne pomocniki do klas podstawowych Ruby. W szczególności mamy metodę Array # add_all , która automatycznie spłaszczy tablice wielowymiarowe przed wykonaniem concat.

Corlew Solutions
źródło
1
Zwykle potrzebujesz niezmienności, więc lepszym pomysłem jest utworzenie nowej tablicy.
vasilakisfil
5
„Zwykle chcesz niezmienność” nie jest dokładne. Przez ponad 20 lat ciągłego rozwoju oprogramowania codziennie pracowałem z wszystkimi rodzajami tablic i kolekcji. Czasami modyfikujesz istniejącą tablicę na miejscu. Czasami musisz pracować z nową instancją.
Corlew Solutions,
35

Prosta metoda, która działa z wersją Ruby> = 2.0, ale nie ze starszymi wersjami:

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
źródło
2
@Ikuty To zdecydowanie najbardziej eleganckie rozwiązanie, jakie znalazłem. Czy możesz wyjaśnić, co się *tutaj dzieje ?
Abhinay,
@Abhinay operator plat rozbija tablicę na elementy, tworząc w ten sposób tablicę jednowymiarową w ostatnim wierszu.
Omar Ali,
[*a, *b]kończy się niepowodzeniem dla starszych wersji ruby, tj. 1.8.7. I o ile Ruby chce powiedzieć, że nie ma już życia, RHEL6 jest nadal utrzymywany, co czyni Ruby 1.8 bardzo znaczącą wersją docelową.
Otheus
1
Nie sądzę, żeby to uzasadniało -1, że otrzymano tę odpowiedź. Brak wersji ruby ​​wspomnianej przez OP, wersja ruby ​​wyraźnie wymieniona w odpowiedzi, więc ... chcesz być wstecznie kompatybilny z wersją starszą niż alfa 0.0.0.0.1? Jest to jedno z dobrych rozwiązań, w zależności od wersji ruby
Ludovic Kuty
1
Wystarczy zaznaczyć, że ta odpowiedź jest bardzo „podobna” do bardzo idiomatycznego JavaScript ES6, w którym można to zrobić [...array1, ...array2], po prostu pamiętając, że zamiast tego splatbyłby operator w języku ruby . Ułatwia to zapamiętanie*...
sandre89
34

Spróbuj, połączy tablice usuwając duplikaty

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

array3 = array1|array2

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

Dalsza dokumentacja znajduje się w części „Ustawianie Unii”

g00se0ne
źródło
Jest to lub zwraca tablicę bez zduplikowanych elementów, oto przykład, w jaki sposób prawdopodobnie nie robi tego, o co pyta, dwa „baz” w pierwszej tablicy zamieniają się w jeden, a „pasek” w drugiej tablicy nie dodaje się. array1 = [„foo”, „bar”, „baz”, „baz”] array2 = [„foo1”, „bar1”, „bar”] array3 = array1 | array2 array3 # => [„foo”, „bar ”,„ baz ”,„ foo1 ”,„ bar1 ”]
Joshua Cheek
Lub jeszcze lepiej:array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
Joshua Pinter
33

Oto dwa sposoby, zauważ w tym przypadku, że pierwszy sposób przypisuje nową tablicę (przekłada się na 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
źródło
24
a = ["some", "thing"]
b = ["another", "thing"]

Aby dołączyć bdo ai zapisać wynik w a:

a.push(*b)

lub

a += b

W obu przypadkach astaje się:

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

ale w pierwszym przypadku elementy bsą dołączane do istniejącej atablicy, aw drugim przypadku dwie tablice są łączone razem, a wynik jest przechowywany w a.

snibbets
źródło
2
Pamiętaj, że a.push(*b)nie jest dokładnie taki sam jak a += b. Ten pierwszy dodaje nowe elementy do istniejącej tablicy; ten ostatni tworzy nową tablicę ze wszystkimi elementami i przypisuje ją do a. Możesz zobaczyć różnicę, jeśli zrobisz coś takiego, aa = aaby zapisać odwołanie aprzed którąkolwiek z metod dołączania, a następnie sprawdź aapóźniej. W pierwszym przypadku zmienia się z nową wartością a, aw drugim pozostaje niezmieniony.
Dave Hartnoll
20

(array1 + array2).uniq

W ten sposób dostajesz najpierw elementy tablicy1. Nie dostaniesz duplikatów.

slindsey3000
źródło
9

Opracowując odpowiedź @ Pilcrow, jedyną odpowiednią odpowiedzią dla ogromnych tablic jest concat( +), ponieważ jest szybka i nie przydziela nowego obiektu do zrzucenia elementów bezużytecznych podczas pracy w pętli.

Oto punkt odniesienia:

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

Wyniki:

       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)

Jak widać użycie pushrzuca BŁĄD : stack level too deep (SystemStackError)gdy tablice są wystarczająco duże.

juliangonzalez
źródło
8

Zasadniczo pytanie brzmi: „jak łączyć tablice w Rubim”. Oczywiście odpowiedzią jest użycie concatlub +jak wspomniano w prawie każdej odpowiedzi.

Naturalnym rozszerzeniem tego pytania byłoby „jak wykonać wierszowanie konkatenacji tablic 2D w Rubim”. Kiedy przejrzałem „rubinowe konkatenowane matryce”, to SO pytanie było najlepszym wynikiem, więc pomyślałem, że zostawię moją odpowiedź na to (nie zadane, ale powiązane) pytanie tutaj dla potomności.


W niektórych aplikacjach możesz chcieć „połączyć” dwie tablice 2D w rzędy. Coś jak,

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

Jest to coś w rodzaju „powiększania” macierzy. Na przykład wykorzystałem tę technikę, aby utworzyć pojedynczą macierz przylegania do reprezentowania wykresu z szeregu mniejszych macierzy. Bez tej techniki musiałbym iterować komponenty w sposób, który mógłby być podatny na błędy lub frustrujący do myślenia. Być może musiałbym each_with_indexna przykład zrobić . Zamiast tego połączyłem zip i spłaszczyć w następujący sposób,

# 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
źródło
8

Po prostu inny sposób na zrobienie tego.

[somearray, anotherarray].flatten
=> ["some", "thing", "another", "thing"]
Datt
źródło
flattenrekurencyjnie spłaszcza wszystko tak daleko, jak to możliwe. Nawet zagnieżdżone tablice. W konsekwencji, jeśli somearraylub anotherarrayzawiera zagnieżdżone tablice, one również ulegają spłaszczeniu. Jest to efekt uboczny, który zwykle nie jest zamierzony.
Hagello
5

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

samg
źródło
Nie wiem o wydajności, ale działa to dla Ruby 1.8. Ogólnie rzecz biorąc, [*a] + [*b]działa
Otheus
Nie sądzę, "another" + "thing"żeby działało zgodnie z oczekiwaniami.
Alexis Wilke,
5

Jeśli nowe dane mogą być tablicą lub skalarem, a chcesz zapobiec zagnieżdżeniu nowych danych, jeśli była to tablica, operator splat jest niesamowity! Zwraca skalar dla skalara i rozpakowaną listę argumentów dla tablicy.

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
źródło
4

Dziwię się, że nikt o tym nie wspominał reduce, co działa dobrze, gdy masz szereg tablic:

lists = [["a", "b"], ["c", "d"]]
flatlist = lists.reduce(:+)  # ["a", "b", "c", "d"]
ScottJ
źródło
4
a = ['a', 'b']
b = ['c', 'd']
arr = [a, b].flatten

To nie usunie duplikatów, ale

a|b

usuwa dups.

AustintheCleric
źródło
Uwaga: Rekurencyjnie spłaszcza również wszystkie wewnętrzne tablice.
Mirodinho
2

Łatwiej jest mi pchać lub dodawać tablice, a następnie spłaszczyć je w miejscu, w ten sposób:

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
źródło
2

somearray = [„some”, „thing”]

anotherarray = [„another”, „thing”]

somearray + anotherarray

Lyle Dickie
źródło