Ajouter un élément à un tableau s'il n'y est pas déjà


92

J'ai une classe Ruby

class MyClass
  attr_writer :item1, :item2
end

my_array = get_array_of_my_class() #my_array is an array of MyClass
unique_array_of_item1 = []

Je veux pousser MyClass#item1à unique_array_of_item1, mais seulement si unique_array_of_item1ne contient pas item1encore. Il existe une solution simple que je connais: il suffit de parcourir my_arrayet de vérifier si unique_array_of_item1contient déjà le courant item1ou non.

Existe-t-il une solution plus efficace?

Réponses:


82

Vous pouvez utiliser Set au lieu de Array.


S'il est vrai que la documentation dit que les ensembles ne sont pas d'ordre, ils sont en fait (à partir de Ruby 1.9) ordonnés. Si vous regardez le code, les principales méthodes que vous utiliseriez pour obtenir la commande (telles que Set#eachet Set#to_a) sont déléguées à @hash. Et à partir de Ruby 1.9, des hachages sont commandés. "Les hachages énumèrent leurs valeurs dans l'ordre d'insertion des clés correspondantes." ruby-doc.org/core-1.9.1/Hash.html
phylae

Jamais nouveau, il n'y avait un ensemble. Ils sont géniaux, merci beaucoup
Brad

123

@Coorasse a une bonne réponse , même si elle devrait être:

my_array | [item]

Et pour mettre à jour my_arrayen place:

my_array |= [item]

63
ou my_array |= [item]qui sera mis à jour my_arrayen place
andorov

2
Il me manque peut-être quelque chose ici, mais l'opérateur | = ne semble pas fonctionner pour moi? J'utilise Ruby 2.1.1
Viet

@Viet |=fonctionne très bien dans mes tests avec 2.1.1. Veuillez décrire votre scénario de test ou ouvrir une nouvelle question.
depquid

Tester à nouveau et cela fonctionne maintenant. Je ne sais pas ce que je faisais plus tôt puisque mon commentaire a été fait il y a plusieurs mois.
Viet

1
Quelle est la complexité de cela?
Nobita

40

Vous n'avez pas besoin de parcourir à la my_arraymain.

my_array.push(item1) unless my_array.include?(item1)

Éditer:

Comme le souligne Tombart dans son commentaire, l'utilisation Array#include?n'est pas très efficace. Je dirais que l'impact sur les performances est négligeable pour les petits tableaux, mais vous voudrez peut-être opter Setpour les plus grands.


6
vous ne voulez certainement pas faire ça! array.include?(item)a de la complexité O(n)- c'est donc comme une itération de l'ensemble du tableau. jetez un œil à ce benchmark: gist.github.com/deric/4953652
Tombart

32

Vous pouvez convertir item1 en tableau et les joindre:

my_array | [item1]

1
Cela ne devrait |pas être ||(voir la réponse de Jason)
Seth

1
Ma faute. Désolé. Modifié la réponse
coorasse

3

Il est important de garder à l'esprit que la classe Set et le | La méthode (également appelée "Set Union") donnera un tableau d' éléments uniques , ce qui est génial si vous ne voulez pas de doublons, mais ce qui sera une mauvaise surprise si vous avez des éléments non uniques dans votre tableau d'origine par conception.

Si vous avez au moins un élément dupliqué dans votre tableau d'origine que vous ne voulez pas perdre, itérer dans le tableau avec un retour anticipé est le pire des cas O (n), ce qui n'est pas si mal dans le grand schéma des choses .

class Array
  def add_if_unique element
    return self if include? element
    push element
  end
end

0

Je ne sais pas si c'est la solution parfaite, mais a fonctionné pour moi:

    host_group = Array.new if not host_group.kind_of?(Array)
    host_group.push(host)
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.