Comment ajoutez-vous un tableau à un autre tableau dans Ruby et ne vous retrouvez-vous pas avec un résultat multidimensionnel?


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

anotherarray = ["another", "thing"]

somearray.push(anotherarray.flatten!)

j'esperais

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

6
Cela vaut la peine de dire (non pas pour vous causer du chagrin, mais parce qu'il vous mordra encore et encore) que votre attente est le problème ici. Les tableaux Ruby (contrairement aux tableaux en Perl par exemple) ne s'aplatissent pas automatiquement dans des contextes comme celui-ci. Ce n'est pas un bug: c'est une fonctionnalité.
Télémaque

3
ri Array@flatten!Pourquoi cette question obtient-elle autant de votes? Le doc est explicite Array#flatten! Flattens self in place. Renvoie nil si aucune modification n'a été apportée (c'est-à-dire que le tableau ne contient aucun sous-tableau.)
yeyo

7
Les questions suscitent des votes positifs si elles sont utiles aux utilisateurs. Les questions les plus simples obtiennent le plus de votes car elles sont utiles au plus grand nombre.
Ziggy

@yeyo, ne pensez-vous pas simplement que l'opération d'aplatissement est gratuite?
Konstantin

@Konstantin op ne cherche pas d'alternatives ou ne parle pas de problèmes de performance, op s'attendait à un résultat qu'il ou elle n'a pas obtenu parce que flatten!cela ne fonctionne pas comme ça. Enfin, la question reflète un problème de logique plutôt qu'un problème d'optimisation. Voir la réponse de pilcrow ci-dessous pour en savoir plus.
yeyo

Réponses:


714

Vous avez une idée réalisable, mais #flatten!est au mauvais endroit - il aplatit son récepteur, de sorte que vous pouvez l' utiliser pour transformer [1, 2, ['foo', 'bar']]en [1,2,'foo','bar'].

J'oublie sans doute certaines approches, mais vous pouvez concaténer :

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

ou ajouter / ajouter :

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

ou épissure :

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

ou ajouter et aplatir :

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

17
bravo pour être le seul (sur 5 je peux voir) qui a fait remarquer ce qui n'allait pas avec le code présenté. +1
Mike Woodhouse

53
L'utilisation de push au lieu de concat évite la création d'un troisième tableau, c'est donc préférable pour les grands tableaux.
phatmann

8
J'adore la poussée avec l'astérisque. Très élégant.
orourkedd

14
@phatmann Concaténation avec Array#concatn'alloue pas de nouveau tableau, Concaténation avec le Array#+fait
cbliard

5
La seule chose qui manque à cette réponse est des comparaisons de référence de chaque approche. +1!
Terra Ashley

206

Vous pouvez simplement utiliser l' +opérateur!

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]

Vous pouvez tout lire sur la classe array ici: http://ruby-doc.org/core/classes/Array.html


15
L'affiche voulait savoir comment concaténer un tableau existant, pas créer un nouveau tableau qui était l'union de deux tableaux.
phatmann

1
Remarque: a+= bcrée un nouveau tableau:c = a = [1,2] ; b = [3,4] ; a += b ; puts c #=> [1,2]
kbrock

1
@kbrock Correct. Si vous traitez de grands tableaux, vous voudrez regarder la pushméthode décrite par @pilcrow.
Joshua Pinter

2
rappelez-vous que +=crée un nouvel objet. dans un tel exemple, un [1, 2].each_with_object([]) { |number, object| object+=number }tableau vide []sera retourné
Filip Bartuzi

1
L'élément ajouté doit être un tableau
RousseauAlexandre

66

L'approche la plus propre consiste à utiliser la méthode Array # concat ; il ne créera pas de nouveau tableau (contrairement à Array # + qui fera la même chose mais créera un nouveau tableau).

Directement à partir des documents ( http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-concat ):

concat (other_ary)

Ajoute les éléments de other_ary à soi.

Donc

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

Array # concat n'aplatira pas un tableau multidimensionnel s'il est passé en argument. Vous devrez gérer cela séparément:

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

Enfin, vous pouvez utiliser notre gem corelib ( https://github.com/corlewsolutions/corelib ) qui ajoute des aides utiles aux classes de base Ruby. En particulier, nous avons une méthode Array # add_all qui aplatira automatiquement les tableaux multidimensionnels avant d'exécuter le concat.


1
Vous voulez généralement l'immuabilité, donc la création d'un nouveau tableau est une meilleure idée.
vasilakisfil

5
"Vous voulez généralement l'immuabilité" n'est pas exact. En plus de 20 ans de développement de logiciels à plein temps, j'ai travaillé quotidiennement avec toutes sortes de matrices et de collections. Parfois, vous modifiez un tableau existant sur place. Parfois, vous devez travailler avec une nouvelle instance.
Corlew Solutions du

35

Méthode simple qui fonctionne avec la version Ruby> = 2.0 mais pas avec les versions plus anciennes:

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 C'est de loin la solution la plus élégante que j'ai trouvée, pouvez-vous expliquer ce qui se passe *ici?
Abhinay

@Abhinay, l'opérateur plat explose le tableau en éléments, créant ainsi un tableau à une dimension dans la dernière ligne.
Omar Ali

[*a, *b]échoue pour les anciennes versions de ruby, c'est-à-dire 1.8.7. Et autant que Ruby veut vous dire qu'il est hors de vie, RHEL6 est toujours maintenu, ce qui fait de Ruby 1.8 une version cible importante.
Otheus

1
Je ne pense pas que cela justifie le -1 obtenu par cette réponse. Pas de version rubis mentionnée par OP, version rubis explicitement mentionnée dans la réponse, donc ... vous voulez être rétrocompatible avec la version pré alpha 0.0.0.0.1? C'est l'une des bonnes solutions, selon la version rubis
Ludovic Kuty

1
Juste pour souligner que cette réponse est très «similaire» au JavaScript ES6 très idiomatique dans lequel vous pourriez le faire [...array1, ...array2], en vous rappelant simplement que l' splatopérateur dans ruby ​​serait à la *place de .... Il est plus facile de se souvenir
sandre89

34

Essayez ceci, il combinera vos tableaux en supprimant les doublons

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

array3 = array1|array2

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

Pour plus de documentation, regardez "Set Union"


Ceci est un ou, il renvoie un tableau sans éléments en double, voici un exemple de la façon dont il ne fait probablement pas ce qu'il demande, les deux "baz" du premier tableau sont transformés en un seul, et la "barre" dans le second tableau n'est pas ajouté. array1 = ["foo", "bar", "baz", "baz"] array2 = ["foo1", "bar1", "bar"] array3 = array1 | array2 array3 # => ["foo", "bar "," baz "," foo1 "," bar1 "]
Joshua Cheek

Ou encore mieux:array1 |= [ "foo1", "bar1" ] #=> [ "foo", "bar", "foo1", "bar1" ]
Joshua Pinter le

33

Voici deux façons, notez dans ce cas que la première façon assigne un nouveau tableau (se traduit par 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"]

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

Pour ajouter bà aet stocker le résultat dans a:

a.push(*b)

ou

a += b

Dans les deux cas, adevient:

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

mais dans le premier cas, les éléments de bsont ajoutés au atableau existant , et dans le second cas, les deux tableaux sont concaténés ensemble et le résultat est stocké dans a.


2
Notez que ce a.push(*b)n'est pas exactement le même que a += b. Le premier ajoute les nouveaux éléments au tableau existant; ce dernier crée un nouveau tableau avec tous les éléments et l'assigne à a. Vous pouvez voir la différence si vous faites quelque chose comme aa = aenregistrer la référence aavant l'une ou l'autre méthode d'ajout, puis l'examiner aaensuite. Dans le premier cas, il change avec la nouvelle valeur de a, et dans le second, il reste inchangé.
Dave Hartnoll

20

(array1 + array2).uniq

De cette façon, vous obtenez d'abord les éléments array1. Vous n'aurez aucun doublon.


9

En élaborant la réponse de @ Pilcrow, la seule réponse appropriée pour les énormes tableaux est concat( +) car elle est rapide et n'alloue pas un nouvel objet à récupérer les ordures lors de son fonctionnement dans une boucle.

Voici la référence:

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

Résultats:

       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)

Comme vous pouvez le voir en utilisant pushjette une ERREUR : stack level too deep (SystemStackError)lorsque les tableaux sont assez grands.


8

La question, essentiellement, est "comment concaténer des tableaux en Ruby". Naturellement, la réponse est d'utiliser concatou +comme mentionné dans presque toutes les réponses.

Une extension naturelle de la question serait "comment effectuer la concaténation ligne par ligne des tableaux 2D dans Ruby". Lorsque j'ai googlé "matrices de concaténation rubis", cette question SO était le meilleur résultat, alors j'ai pensé laisser ma réponse à cette question (non demandée mais liée) ici pour la postérité.


Dans certaines applications, vous souhaiterez peut-être "concaténer" deux tableaux 2D en ligne. Quelque chose comme,

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

C'est quelque chose comme "augmenter" une matrice. Par exemple, j'ai utilisé cette technique pour créer une seule matrice d'adjacence pour représenter un graphique à partir d'un tas de matrices plus petites. Sans cette technique, j'aurais dû parcourir les composants d'une manière qui aurait pu être source d'erreurs ou frustrante. J'aurais peut-être dû faire un each_with_index, par exemple. Au lieu de cela, j'ai combiné zip et aplatir comme suit,

# 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

Juste une autre façon de le faire.

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

flattenaplatit tout autant que possible, récursivement. Même des tableaux imbriqués. Par conséquent, si somearrayou anotherarraycontient des tableaux imbriqués, ils s'aplatissent également. Il s'agit d'un effet secondaire qui n'est généralement pas prévu.
hagello

5

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


Je ne connais pas l'efficacité, mais cela fonctionne pour Ruby 1.8. En général, les [*a] + [*b]travaux
Otheus

Je ne pense pas que cela "another" + "thing"fonctionnera comme prévu.
Alexis Wilke

5

Si les nouvelles données peuvent être un tableau ou un scalaire et que vous souhaitez empêcher l'imbrication des nouvelles données s'il s'agissait d'un tableau, l'opérateur splat est génial! Il retourne un scalaire pour un scalaire et une liste décompactée d'arguments pour un tableau.

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

Je suis surpris que personne ne l'ait mentionné reduce, ce qui fonctionne bien lorsque vous disposez d'un tableau de tableaux:

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

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

Cela ne supprimera pas les doublons, mais

a|b

supprime les doublons.


Remarque: Cela aplatit récursivement tous les tableaux internes également.
Mirodinho

2

Je trouve plus facile de pousser ou d'ajouter des tableaux, puis de les aplatir en place, comme suit:

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 = ["certains", "chose"]

anotherarray = ["autre", "chose"]

un tableau + un autre tableau

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.