Comment vérifier si une valeur existe dans un tableau dans Ruby


1315

J'ai une valeur 'Dog'et un tableau ['Cat', 'Dog', 'Bird'].

Comment puis-je vérifier s'il existe dans le tableau sans le parcourir? Existe-t-il un moyen simple de vérifier si la valeur existe, rien de plus?


5
utiliser le .include? méthode . Il renvoie un booléen qui est ce que vous voulez. Dans votre cas, tapez simplement: ['Cat', 'Dog', 'Bird']. Include ('Dog') et il devrait retourner le booléen true.
Jwan622

ne pas utiliser d' inclure? si vous voulez vérifier plusieurs fois si une valeur différente est présente dans le tableau ou non car include? chaque fois itérera sur le tableau en prenant l'opération O (n) pour rechercher à chaque fois, faites plutôt un hachage hash = arr.map {|x| [x,true]}.to_h, vérifiez maintenant si hash.has_key? 'Dog' retourne vrai ou non
aqfaridi

3
Vous ne pouvez pas vraiment le faire totalement "sans le parcourir". C'est logiquement impossible, l'ordinateur ne peut tout simplement pas savoir avec certitude si le tableau contient l'élément sans parcourir les éléments pour vérifier si l'un d'eux est celui qu'il recherche. À moins qu'il ne soit vide, bien sûr. Alors je suppose que vous n'avez pas besoin d'une boucle.
Tim M.

Voir les repères ci-dessous pour des tests de la différence des différentes façons de trouver un élément dans un tableau et un ensemble. stackoverflow.com/a/60404934/128421
The Tin Man

Réponses:


1946

Vous recherchez include?:

>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true

73
Syntaxe alternative:%w(Cat Dog Bird).include? 'Dog'
scarver2

184
Parfois, je souhaite que ce soit "contient" pas inclure. Je le mélange toujours avec des inclusions.
Henley Chiu

19
Permettez-moi de noter qu'en interne, le #include?bouclage s'effectue toujours. Cependant, le codeur est sauvé de l'écriture explicite de la boucle. J'ai ajouté une réponse qui exécute la tâche vraiment sans boucle.
Boris Stitnicky

8
@HenleyChiu I qui l'appelait[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'

4
@AlfonsoVergara Oui, toute solution pour un tableau doit faire une sorte de boucle en interne; il n'y a aucun moyen de tester l'appartenance à un tableau sans boucle. Si vous ne voulez pas faire de boucle, même en interne, vous devez utiliser une structure de données différente, comme une table de hachage parfaite avec des clés de taille fixe. Étant donné qu'il n'y a aucun moyen de tester l'appartenance à un tableau sans boucler en interne, j'ai interprété la question comme signifiant "sans avoir à écrire la boucle explicitement moi-même"
Brian Campbell

250

Il existe une in?méthode dans ActiveSupport(partie de Rails) depuis la v3.1, comme l'a souligné @campaterson. Donc dans Rails, ou si vous require 'active_support', vous pouvez écrire:

'Unicorn'.in?(['Cat', 'Dog', 'Bird']) # => false

OTOH, il n'y a aucun inopérateur ou #in?méthode dans Ruby lui-même, même s'il a été proposé auparavant, en particulier par Yusuke Endoh, un membre de premier ordre de ruby-core.

Comme l' a souligné par d' autres, la méthode inverse include?existe, pour tous Enumerables , y compris Array, Hash, Set, Range:

['Cat', 'Dog', 'Bird'].include?('Unicorn') # => false

Notez que si vous avez plusieurs valeurs dans votre tableau, elles seront toutes vérifiées l'une après l'autre (ie O(n)), tandis que la recherche d'un hachage sera à temps constant (ie O(1)). Ainsi, si votre tableau est constant, par exemple, c'est une bonne idée d'utiliser un ensemble à la place. Par exemple:

require 'set'
ALLOWED_METHODS = Set[:to_s, :to_i, :upcase, :downcase
                       # etc
                     ]

def foo(what)
  raise "Not allowed" unless ALLOWED_METHODS.include?(what.to_sym)
  bar.send(what)
end

Un test rapide révèle que l'appel include?à un élément 10 Setest environ 3,5 fois plus rapide que l'appel à l'équivalent Array(si l'élément n'est pas trouvé).

Une dernière note de clôture: méfiez-vous lors de l'utilisation include?sur un Range, il y a des subtilités, alors référez-vous au doc et comparez avec cover?...


2
Bien que Ruby ne soit pas inclus #in?dans son noyau, si vous utilisez Rails, il est disponible. api.rubyonrails.org/classes/Object.html#method-i-in-3F (Je sais que c'est une question Ruby, pas Rails, mais cela peut aider quiconque cherche à utiliser #in?dans Rails. On dirait qu'il a été ajouté dans Rails 3.1 apidock.com/rails/Object/in%3F
campeterson

166

Essayer

['Cat', 'Dog', 'Bird'].include?('Dog')

c'est l'ancienne syntaxe, regardez ^^^ @ la réponse de brian
jahrichie

62
@jahrichie que considérez-vous exactement "ancienne syntaxe" dans cette réponse, les parenthèses facultatives?
Dennis

3
je suis d'accord @Dennis, ce n'est pas plus ancien, les parenthèses sont facultatives et dans la plupart des cas une BONNE PRATIQUE .... essayez d'utiliser include sans parenthèses sur une seule ligne si la phrase par exemple, ce que je voulais dire, c'est que selon votre cas vous devrait ou doit utiliser des crochets ou non (pas du tout lié aux "anciennes" syntaxes rubis)
d1jhoni1b

C'est la seule syntaxe que j'ai pu utiliser au sein d'une opération ternaire.
Michael Cameron

49

Utilisation Enumerable#include:

a = %w/Cat Dog Bird/

a.include? 'Dog'

Ou, si un certain nombre de tests sont effectués, 1 vous pouvez vous débarrasser de la boucle (qui a même include?) et passer de O (n) à O (1) avec:

h = Hash[[a, a].transpose]
h['Dog']


1. J'espère que cela est évident, mais pour éviter les objections: oui, pour quelques recherches seulement, les op Hash [] et transpose dominent le profil et sont chacun O (n) eux-mêmes.


49

Si vous souhaitez vérifier par un bloc, vous pouvez essayer any?ou all?.

%w{ant bear cat}.any? {|word| word.length >= 3}   #=> true  
%w{ant bear cat}.any? {|word| word.length >= 4}   #=> true  
[ nil, true, 99 ].any?                            #=> true  

Voir Enumerable pour plus d'informations.

Mon inspiration est venue de " évaluer si le tableau contient des éléments en rubis "


1
Très utile si vous voulez vérifier qu'une / toutes ces chaînes sont incluses dans une autre chaîne / constante
thanikkal

43

Ruby a onze méthodes pour rechercher des éléments dans un tableau.

La préférence est include?ou, pour les accès répétés, créez un ensemble, puis appelez include?ou member?.

Voici tous:

array.include?(element) # preferred method
array.member?(element)
array.to_set.include?(element)
array.to_set.member?(element)
array.index(element) > 0
array.find_index(element) > 0
array.index { |each| each == element } > 0
array.find_index { |each| each == element } > 0
array.any? { |each| each == element }
array.find { |each| each == element } != nil
array.detect { |each| each == element } != nil

Ils renvoient tous une truevaleur ish si l'élément est présent.

include?est la méthode préférée. Il utilise une forboucle de langage C en interne qui se casse lorsqu'un élément correspond aux rb_equal_opt/rb_equalfonctions internes . Il ne peut être beaucoup plus efficace que si vous créez un ensemble pour les vérifications d'appartenance répétées.

VALUE
rb_ary_includes(VALUE ary, VALUE item)
{
  long i;
  VALUE e;

  for (i=0; i<RARRAY_LEN(ary); i++) {
    e = RARRAY_AREF(ary, i);
    switch (rb_equal_opt(e, item)) {
      case Qundef:
        if (rb_equal(e, item)) return Qtrue;
        break;
      case Qtrue:
        return Qtrue;
    }
  }
  return Qfalse;
}

member?n'est pas redéfini dans la Arrayclasse et utilise une implémentation non optimisée du Enumerablemodule qui énumère littéralement tous les éléments:

static VALUE
member_i(RB_BLOCK_CALL_FUNC_ARGLIST(iter, args))
{
  struct MEMO *memo = MEMO_CAST(args);

  if (rb_equal(rb_enum_values_pack(argc, argv), memo->v1)) {
    MEMO_V2_SET(memo, Qtrue);
    rb_iter_break();
  }
  return Qnil;
}

static VALUE
enum_member(VALUE obj, VALUE val)
{
  struct MEMO *memo = MEMO_NEW(val, Qfalse, 0);

  rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
  return memo->v2;
}

Traduit en code Ruby, cela permet:

def member?(value)
  memo = [value, false, 0]
  each_with_object(memo) do |each, memo|
    if each == memo[0]
      memo[1] = true 
      break
    end
  memo[1]
end

Les deux include?et member?ont une complexité temporelle O (n) car les deux recherchent dans le tableau la première occurrence de la valeur attendue.

Nous pouvons utiliser un ensemble pour obtenir le temps d'accès O (1) au prix d'avoir à créer d'abord une représentation de hachage du tableau. Si vous vérifiez à plusieurs reprises l'appartenance à la même gamme, cet investissement initial peut être rentable rapidement. Setn'est pas implémenté en C mais en tant que classe Ruby simple, le temps d'accès O (1) du sous-jacent en @hashvaut la peine.

Voici l'implémentation de la classe Set:

module Enumerable
  def to_set(klass = Set, *args, &block)
    klass.new(self, *args, &block)
  end
end

class Set
  def initialize(enum = nil, &block) # :yields: o
    @hash ||= Hash.new
    enum.nil? and return
    if block
      do_with_enum(enum) { |o| add(block[o]) }
    else
      merge(enum)
    end
  end

  def merge(enum)
    if enum.instance_of?(self.class)
      @hash.update(enum.instance_variable_get(:@hash))
    else
      do_with_enum(enum) { |o| add(o) }
    end
    self
  end

  def add(o)
    @hash[o] = true
    self
  end

  def include?(o)
    @hash.include?(o)
  end
  alias member? include?

  ...
end

Comme vous pouvez le voir, la classe Set crée simplement une @hashinstance interne , mappe tous les objets trueet vérifie ensuite l'appartenance en utilisant Hash#include?ce qui est implémenté avec le temps d'accès O (1) dans la classe Hash.

Je ne discuterai pas des sept autres méthodes car elles sont toutes moins efficaces.

Il existe en fait encore plus de méthodes avec une complexité O (n) au-delà des 11 énumérées ci-dessus, mais j'ai décidé de ne pas les répertorier car elles analysent l'intégralité du tableau plutôt que de les casser lors de la première correspondance.

Ne les utilisez pas:

# bad examples
array.grep(element).any? 
array.select { |each| each == element }.size > 0
...

Comme c'est effronté de dire que Ruby a exactement 11 façons de faire quoi que ce soit! Dès que vous dites que quelqu'un vous fera remarquer que vous avez raté le n ° 12, puis le n ° 13, etc. Pour faire valoir mon point de vue, je suggérerai d'autres moyens, mais permettez-moi d'abord de remettre en question les 11moyens que vous avez énumérés. Tout d'abord, vous pouvez difficilement compter indexet find_index(ou findet detect) comme des méthodes distinctes, car ce ne sont que des noms différents pour la même méthode. Deuxièmement, toutes les expressions qui se terminent par > 0sont incorrectes, ce qui, j'en suis sûr, était un oubli. (suite)
Cary Swoveland

... arr.index(e), par exemple, renvoie 0if arr[0] == e. Vous vous souviendrez des arr.index(e)retours nils'il en'est pas présent. indexne peut pas être utilisé, cependant, si l'on cherche nildans arr. (Même problème avec rindex, qui n'est pas répertorié.). Convertir le tableau en un ensemble, puis utiliser des méthodes d'ensemble est un peu extensible. Pourquoi ne pas ensuite convertir en hachage (avec des clés du tableau et des valeurs arbitraires), puis utiliser des méthodes de hachage? Même si la conversion en un ensemble est OK, d'autres méthodes d'ensemble peuvent être utilisées, telles que !arr.to_set.add?(e). (suite)
Cary Swoveland

... Comme promis, voici quelques autres méthodes qui pourraient être utilisées: arr.count(e) > 0, arr != arr.dup.delete(e) , arr != arr - [e]et arr & [e] == [e]. On pourrait également employer selectet reject.
Cary Swoveland du

Je recommande fortement d'utiliser un ensemble si le critère est qu'aucun doublon n'est autorisé et de savoir si un élément particulier existe dans la liste. L'ensemble est tellement plus rapide. Les tableaux ne devraient vraiment pas être utilisés lorsque la recherche est nécessaire; Ils sont mieux utilisés comme une file d'attente où les choses sont temporairement stockées pour être traitées dans l'ordre. Hash et Set sont meilleurs pour regarder si quelque chose existe.
The Tin Man

30

Plusieurs réponses suggèrent Array#include?, mais il y a une mise en garde importante: En regardant la source, même Array#include?effectuer une boucle:

rb_ary_includes(VALUE ary, VALUE item)
{
    long i;

    for (i=0; i<RARRAY_LEN(ary); i++) {
        if (rb_equal(RARRAY_AREF(ary, i), item)) {
            return Qtrue;
        }
    }
    return Qfalse;
}

La façon de tester la présence du mot sans boucle est de construire un trie pour votre tableau. Il existe de nombreuses implémentations de trie (google "ruby trie"). Je vais utiliser rambling-triedans cet exemple:

a = %w/cat dog bird/

require 'rambling-trie' # if necessary, gem install rambling-trie
trie = Rambling::Trie.create { |trie| a.each do |e| trie << e end }

Et maintenant, nous sommes prêts à tester la présence de différents mots dans votre tableau sans boucler dessus, dans le O(log n)temps, avec la même simplicité syntaxique que Array#include?, en utilisant sublinéaire Trie#include?:

trie.include? 'bird' #=> true
trie.include? 'duck' #=> false

8
a.each do ... endUmm ...
je

27
Notez que cela inclut en fait une boucle; tout ce qui n'est pas O (1) inclut une sorte de boucle. Il s'agit simplement d'une boucle sur les caractères de la chaîne d'entrée. Notez également qu'une réponse déjà mentionnée Set#include?pour les personnes soucieuses d'efficacité; couplé à l'utilisation de symboles au lieu de chaînes, il peut s'agir d'un cas moyen O (1) (si vous utilisez des chaînes, alors le calcul du hachage est O (n) où n est la longueur de la chaîne). Ou si vous souhaitez utiliser des bibliothèques tierces, vous pouvez utiliser un hachage parfait qui est le pire des cas O (1).
Brian Campbell

4
AFAIK, Setutilise des hachages pour indexer ses membres, donc Set#include? devrait en fait être de complexité O (1) pour un bien distribué Set(plus précisément O (taille d'entrée) pour le hachage, et O (log (n / bucket-number)) pour la recherche)
Uri Agassi

11
Le coût de création et de maintenance du trie est tout aussi élevé. Si vous effectuez de nombreuses opérations de recherche sur le tableau, le coût en mémoire et en temps pour remplir un trie et le maintenir en vaut la peine, mais pour une seule, voire des centaines ou des milliers de vérifications, O (n) convient parfaitement. Une autre option qui ne nécessite pas l'ajout de dépendances serait de trier le tableau ou de le maintenir dans l'ordre trié, auquel cas une opération de recherche binaire O (lg n) peut être utilisée pour vérifier l'inclusion.
speakingcode

1
@speakingcode, vous avez peut-être raison du point de vue pragmatique. Mais l'OP demande de "vérifier si la valeur existe, rien de plus, sans boucle". Lorsque j'ai écrit cette réponse, il y avait de nombreuses solutions pragmatiques ici, mais aucune qui répondrait réellement aux exigences littérales du demandeur. Votre observation selon laquelle les BST sont liés aux essais est correcte, mais pour les chaînes, le trie est le bon outil pour le travail, même Wikipedia en sait beaucoup . La complexité de la construction et de la maintenance d'un tri bien implémenté est étonnamment favorable.
Boris Stitnicky

18

Si vous ne voulez pas faire de boucle, il n'y a aucun moyen de le faire avec des tableaux. Vous devez utiliser un ensemble à la place.

require 'set'
s = Set.new
100.times{|i| s << "foo#{i}"}
s.include?("foo99")
 => true
[1,2,3,4,5,6,7,8].to_set.include?(4) 
  => true

Les ensembles fonctionnent en interne comme Hashs, donc Ruby n'a pas besoin de parcourir la collection pour trouver des éléments, car comme son nom l'indique, il génère des hachages des clés et crée une carte mémoire afin que chaque hachage pointe vers un certain point en mémoire. L'exemple précédent fait avec un Hash:

fake_array = {}
100.times{|i| fake_array["foo#{i}"] = 1}
fake_array.has_key?("foo99")
  => true

L'inconvénient est que les ensembles et les touches de hachage ne peuvent inclure que des éléments uniques et si vous ajoutez beaucoup d'éléments, Ruby devra refaire le tout après un certain nombre d'éléments pour créer une nouvelle carte qui convient à un espace de touches plus grand. Pour en savoir plus, je vous recommande de regarder " MountainWest RubyConf 2014 - Big O in a Homemade Hash par Nathan Long ".

Voici une référence:

require 'benchmark'
require 'set'

array = []
set   = Set.new

10_000.times do |i|
  array << "foo#{i}"
  set   << "foo#{i}"
end

Benchmark.bm do |x|
  x.report("array") { 10_000.times { array.include?("foo9999") } }
  x.report("set  ") { 10_000.times { set.include?("foo9999")   } }
end

Et les résultats:

      user     system      total        real
array  7.020000   0.000000   7.020000 (  7.031525)
set    0.010000   0.000000   0.010000 (  0.004816)

Si vous utilisez la détection, vous pouvez au moins réduire le bouclage. detect s'arrêtera au premier élément «détecté» (le bloc passé pour l'élément est évalué à true). De plus, vous pouvez dire détecter quoi faire si rien n'est détecté (vous pouvez passer dans un lambda).
aenw

1
@aenw ne include?s'arrête pas au premier coup?
Kimmo Lehto

vous avez absolument raison. Je suis tellement habitué à utiliser la détection que j'avais oublié cette option. merci pour votre commentaire - cela m'a permis de rafraîchir mes connaissances.
aenw

include?s'arrête au premier hit mais si ce hit est à la fin de la liste .... Toute solution qui s'appuie sur un tableau pour le stockage aura des performances dégradantes au fur et à mesure que la liste s'allonge, surtout quand il faut trouver un élément à la fin du liste. Hash et Set n'ont pas ce problème, pas plus qu'une liste ordonnée et une recherche binaire.
The Tin Man

C'est à peu près ce que cette réponse était en premier lieu :)
Kimmo Lehto

17

Voici une autre façon de procéder: utilisez la Array#indexméthode.

Il renvoie l'index de la première occurrence de l'élément dans le tableau.

Par exemple:

a = ['cat','dog','horse']
if a.index('dog')
    puts "dog exists in the array"
end

index() peut également prendre un bloc:

Par exemple:

a = ['cat','dog','horse']
puts a.index {|x| x.match /o/}

Cela renvoie l'index du premier mot du tableau qui contient la lettre «o».


indexitère toujours sur le tableau, il retourne juste la valeur de l'élément.
The Tin Man

13

Fait amusant,

Vous pouvez utiliser *pour vérifier l'appartenance à un tableau dans une caseexpression.

case element
when *array 
  ...
else
  ...
end

Notez le peu *dans la clause when, cela vérifie l'appartenance au tableau.

Tout le comportement magique habituel de l'opérateur splat s'applique, donc par exemple, si ce arrayn'est pas réellement un tableau mais un seul élément, il correspondra à cet élément.


Bon à savoir..!
Cary Swoveland

Ce serait également une première vérification lente dans une déclaration de cas, donc je l'emploierais dans le dernier whenpossible afin que d'autres vérifications plus rapides soient éliminées rapidement.
The Tin Man

9

Il existe plusieurs façons d'accomplir cela. Certains d'entre eux sont les suivants:

a = [1,2,3,4,5]

2.in? a  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false

6
Notez que Object#in?n'a été ajouté qu'à Rails (ie ActiveSupport) v3.1 +. Il n'est pas disponible dans le noyau Ruby.
Tom Lord

5

Si vous avez besoin de vérifier plusieurs fois pour n'importe quelle clé, convertissez arren hash, et vérifiez maintenant O (1)

arr = ['Cat', 'Dog', 'Bird']
hash = arr.map {|x| [x,true]}.to_h
 => {"Cat"=>true, "Dog"=>true, "Bird"=>true}
hash["Dog"]
 => true
hash["Insect"]
 => false

Performances de Hash # has_key? contre Array # include?

Paramètre Hash # has_key? Array # include

Complexité temporelle Fonctionnement O (1) Fonctionnement O (n) 

Type d'accès Accède au hachage [clé] s'il parcourt chaque élément
                        retourne alors toute valeur du tableau jusqu'à ce qu'il
                        true est renvoyé à la recherche de la valeur dans Array
                        Hash # has_key? appel
                        appel    

Pour une vérification unique, l'utilisation include?est correcte


N'êtes-vous pas encore en boucle à travers le tableau pour le convertir en hachage?
chrisjacob

2
oui, je l'ai fait mais maintenant j'ai une réponse pré-calculée pour vérifier si un élément existe dans le tableau ou non. Maintenant, la prochaine fois que je chercherai un autre mot, il faudra O (1) pour la requête, bien que le pré-calcul prendra O (n). En fait, j'ai utilisé l'inclusion? dans mon application à l'intérieur de la boucle pour vérifier si un élément particulier existe dans le tableau ou non, cela ruine les performances, il a vérifié dans ruby-prof, c'était le goulot d'étranglement
aqfaridi

1
.... mais de l'espace O (n) et aussi, non c'est du temps O (n), car comme @ chris72205 le souligne à juste titre, vous devez d'abord parcourir votre tableau. O (1) + O (n) = O (n). Donc, en fait, c'est bien pire que d'inclure?
Rambatino

Mec, je disais juste de ne pas utiliser d'inclure la boucle intérieure, pour une vérification unique en utilisant l'inclusion, c'est bien. Veuillez donc d'abord lire le cas d'utilisation, il vaut mieux si vous devez vérifier plusieurs fois à l'intérieur de la boucle.
aqfaridi

La conversion d'un tableau en hachage est coûteuse, mais l'utilisation d'un tableau pour la recherche n'est pas la bonne structure. Les tableaux sont meilleurs que les files d'attente où l'ordre peut être important; Les hachages et les ensembles sont meilleurs lorsque l'inclusion / l'existence / l'unicité sont importantes. Commencez avec le bon conteneur et le problème est théorique.
The Tin Man

4

Cela vous dira non seulement qu'il existe mais aussi combien de fois il apparaît:

 a = ['Cat', 'Dog', 'Bird']
 a.count("Dog")
 #=> 1

12
Cela n'a aucun sens si vous ne voulez pas savoir combien de fois il apparaît, cependant, car .any?il reviendra dès qu'il trouvera le premier élément correspondant, .counttraitera toujours le tableau entier.
Zaz

Bien que cela indique techniquement si quelque chose existe, ce n'est PAS non plus la bonne façon de le faire si vous vous souciez de la vitesse.
The Tin Man

4

Pour ce que ça vaut, les documents Ruby sont une ressource incroyable pour ce genre de questions.

Je prendrais également note de la longueur du tableau que vous recherchez. La include?méthode exécutera une recherche linéaire avec une complexité O (n) qui peut devenir assez moche en fonction de la taille du tableau.

Si vous travaillez avec un grand tableau (trié), j'envisagerais d'écrire un algorithme de recherche binaire qui ne devrait pas être trop difficile et qui présente le pire cas de O (log n).

Ou si vous utilisez Ruby 2.0, vous pouvez en profiter bsearch.


3
Une recherche binaire suppose que le tableau est trié (ou ordonné sous une certaine forme), ce qui peut être coûteux pour les grands tableaux, annulant souvent l'avantage.
The Tin Man

Une recherche binaire nécessite également que toutes les paires d'éléments soient comparables <=>, ce qui n'est pas toujours le cas. Supposons, par exemple, que les éléments du tableau soient des hachages.
Cary Swoveland

1
@CarySwoveland, il devrait être plus ou moins implicite que les éléments d'un tableau trié sont comparables.
davissp14

4

Tu peux essayer:

Exemple: si Cat et Dog existent dans le tableau:

(['Cat','Dog','Bird'] & ['Cat','Dog'] ).size == 2   #or replace 2 with ['Cat','Dog].size

Au lieu de:

['Cat','Dog','Bird'].member?('Cat') and ['Cat','Dog','Bird'].include?('Dog')

Remarque: member?et include?sont les mêmes.

Cela peut faire le travail en une seule ligne!


3

Si nous voulons ne pas utiliser include?cela fonctionne aussi:

['cat','dog','horse'].select{ |x| x == 'dog' }.any?

3
tout? accepte également les blocs: ['chat', 'chien', 'cheval']. {| x | x == 'chien'}
maikonas

3
['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}
=> "Dog"
!['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}.nil?
=> true

['Cat', nil, 'Dog'].detect { |x| x == nil } #=> nil. A été niltrouvé?
Cary Swoveland,

2

Et de cette façon?

['Cat', 'Dog', 'Bird'].index('Dog')

Il va encore parcourir le tableau juste pour trouver l'élément. Il doit ensuite renvoyer l'index de cet élément.
The Tin Man

2

Si vous essayez de le faire dans un test unitaire MiniTest , vous pouvez utiliser assert_includes. Exemple:

pets = ['Cat', 'Dog', 'Bird']
assert_includes(pets, 'Dog')      # -> passes
assert_includes(pets, 'Zebra')    # -> fails 

2

Il y a l'inverse.

Supposons que le tableau soit [ :edit, :update, :create, :show ], enfin peut-être l'ensemble des sept péchés capitaux / mortels .

Et un autre jouet avec l'idée de tirer une action valide d'une chaîne:

"my brother would like me to update his profile"

Alors:

[ :edit, :update, :create, :show ].select{|v| v if "my brother would like me to update his profile".downcase =~ /[,|.| |]#{v.to_s}[,|.| |]/}

1
Votre expression régulière, /[,|.| |]#{v.to_s}[,|.| |]/me fait penser que vous vouliez trouver «le nom de l'action entouré par l'un des: virgule, point, espace ou rien du tout», mais il y a quelques bugs subtils. "|update|"reviendrait [:update]et "update"reviendrait []. Les classes de caractères ( [...]) n'utilisent pas de tuyaux ( |) pour séparer les caractères. Même si nous les changeons en groupes ( (...)), vous ne pouvez pas faire correspondre un caractère vide. Donc, l'expression /(,|\.| |^)#{v.to_s}(,|\.| |$)/
régulière que

le regex est ok (vérifié rubular.com) - et @Rambatino: le pourquoi serait comme Amazon Echo par exemple;) Vous pourriez dire: "veuillez ajouter du poulet à ma liste de courses" (et - eh bien - alors vous devriez ajoutez le: ajoutez au tableau, mais je pense que vous en avez l'essentiel;)
walt_die

1
"le regex est ok (vérifié rubular.com)". Ce n'est pas ok. Votre expression régulière ne correspondra pas à un mot clé au début ou à la fin d'une chaîne (par exemple, "mettre à jour le profil de mon frère"). Si vous ne voulez pas faire correspondre le début ou la fin, alors votre expression régulière n'est toujours pas correcte car la classe de caractères de chaque côté du mot-clé devrait être/[,. ]/
bkDJ

Comme le dit @bkDJ, l'expression régulière est erronée. rubular.com/r/4EG04rANz6KET6
Tin Man

1

Si vous souhaitez renvoyer la valeur non seulement vrai ou faux, utilisez

array.find{|x| x == 'Dog'}

Cela renverra «Dog» s'il existe dans la liste, sinon nul.


Ou utiliser array.any?{|x| x == 'Dog'}si vous ne voulez vrai / faux (pas la valeur), mais souhaitez également comparer contre un bloc comme celui - ci.
mahemoff

0

Voici une autre façon de procéder:

arr = ['Cat', 'Dog', 'Bird']
e = 'Dog'

present = arr.size != (arr - [e]).size

1
C'est une façon horriblement inefficace de le faire! Je ne vote pas vers le bas car ce n'est pas techniquement incorrect, et quelqu'un pourrait apprendre quelque chose sur Ruby en le lisant, mais il y a beaucoup de meilleures réponses ci-dessus.
jibberia

Conehead, vous pouvez simplifier arr != arr - [e]. arr & [e] == [e]est une autre voie dans le même sens.
Cary Swoveland du

@CarySwoveland Ne vous moquez pas d'un chapeau de sorcier ;-)
Wand Maker

Je parlais de la tête du sorcier, pas du chapeau, avec le plus grand respect.
Cary Swoveland


0

il existe de nombreuses façons de trouver un élément dans n'importe quel tableau, mais la manière la plus simple est «in?» méthode.

example:
arr = [1,2,3,4]
number = 1
puts "yes #{number} is present in arr" if number.in? arr

2
Note: Comme indiqué dans cette réponse , la méthode in?doit ActiveSupportêtre importée: require active_support.
Patrick

Il ne nécessite pas tous ActiveSupport si vous utilisez les extensions principales .
The Tin Man

0

si vous ne souhaitez pas l'utiliser, include?vous pouvez d'abord envelopper l'élément dans un tableau, puis vérifier si l'élément encapsulé est égal à l'intersection du tableau et de l'élément encapsulé. Cela renverra une valeur booléenne basée sur l'égalité.

def in_array?(array, item)
    item = [item] unless item.is_a?(Array)
    item == array & item
end

-1

Je trouve toujours intéressant de faire quelques benchmarks pour voir la vitesse relative des différentes façons de faire quelque chose.

La recherche d'un élément de tableau au début, au milieu ou à la fin affectera toutes les recherches linéaires mais affectera à peine une recherche sur un ensemble.

La conversion d'un tableau en ensemble va provoquer un hit dans le temps de traitement, alors créez le jeu à partir d'un tableau une fois, ou commencez par un ensemble depuis le tout début.

Voici le code de référence:

# frozen_string_literal: true

require 'fruity'
require 'set'

ARRAY = (1..20_000).to_a
SET = ARRAY.to_set

DIVIDER = '-' * 20

def array_include?(elem)
  ARRAY.include?(elem)
end

def array_member?(elem)
  ARRAY.member?(elem)
end

def array_index(elem)
  ARRAY.index(elem) >= 0
end

def array_find_index(elem)
  ARRAY.find_index(elem) >= 0
end

def array_index_each(elem)
  ARRAY.index { |each| each == elem } >= 0
end

def array_find_index_each(elem)
  ARRAY.find_index { |each| each == elem } >= 0
end

def array_any_each(elem)
  ARRAY.any? { |each| each == elem }
end

def array_find_each(elem)
  ARRAY.find { |each| each == elem } != nil
end

def array_detect_each(elem)
  ARRAY.detect { |each| each == elem } != nil
end

def set_include?(elem)
  SET.include?(elem)
end

def set_member?(elem)
  SET.member?(elem)
end

puts format('Ruby v.%s', RUBY_VERSION)

{
  'First' => ARRAY.first,
  'Middle' => (ARRAY.size / 2).to_i,
  'Last' => ARRAY.last
}.each do |k, element|
  puts DIVIDER, k, DIVIDER

  compare do
    _array_include?        { array_include?(element)        }
    _array_member?         { array_member?(element)         }
    _array_index           { array_index(element)           }
    _array_find_index      { array_find_index(element)      }
    _array_index_each      { array_index_each(element)      }
    _array_find_index_each { array_find_index_each(element) }
    _array_any_each        { array_any_each(element)        }
    _array_find_each       { array_find_each(element)       }
    _array_detect_each     { array_detect_each(element)     }
  end
end

puts '', DIVIDER, 'Sets vs. Array.include?', DIVIDER
{
  'First' => ARRAY.first,
  'Middle' => (ARRAY.size / 2).to_i,
  'Last' => ARRAY.last
}.each do |k, element|
  puts DIVIDER, k, DIVIDER

  compare do
    _array_include? { array_include?(element) }
    _set_include?   { set_include?(element)   }
    _set_member?    { set_member?(element)    }
  end
end

Ce qui, lorsqu'il est exécuté sur mon ordinateur portable Mac OS, se traduit par:

Ruby v.2.7.0
--------------------
First
--------------------
Running each test 65536 times. Test will take about 5 seconds.
_array_include? is similar to _array_index
_array_index is similar to _array_find_index
_array_find_index is faster than _array_any_each by 2x ± 1.0
_array_any_each is similar to _array_index_each
_array_index_each is similar to _array_find_index_each
_array_find_index_each is faster than _array_member? by 4x ± 1.0
_array_member? is faster than _array_detect_each by 2x ± 1.0
_array_detect_each is similar to _array_find_each
--------------------
Middle
--------------------
Running each test 32 times. Test will take about 2 seconds.
_array_include? is similar to _array_find_index
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 2x ± 0.1
_array_member? is faster than _array_index_each by 2x ± 0.1
_array_index_each is similar to _array_find_index_each
_array_find_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004% ± 10.0%
_array_detect_each is similar to _array_find_each
--------------------
Last
--------------------
Running each test 16 times. Test will take about 2 seconds.
_array_include? is faster than _array_find_index by 10.000000000000009% ± 10.0%
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 3x ± 0.1
_array_member? is faster than _array_find_index_each by 2x ± 0.1
_array_find_index_each is similar to _array_index_each
_array_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004% ± 10.0%
_array_detect_each is similar to _array_find_each

--------------------
Sets vs. Array.include?
--------------------
--------------------
First
--------------------
Running each test 65536 times. Test will take about 1 second.
_array_include? is similar to _set_include?
_set_include? is similar to _set_member?
--------------------
Middle
--------------------
Running each test 65536 times. Test will take about 2 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 1400x ± 1000.0
--------------------
Last
--------------------
Running each test 65536 times. Test will take about 4 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 3000x ± 1000.0

Fondamentalement, les résultats me disent d'utiliser un ensemble pour tout si je recherche l'inclusion, sauf si je peux garantir que le premier élément est celui que je veux, ce qui n'est pas très probable. Il y a des frais généraux lors de l'insertion d'éléments dans un hachage, mais les temps de recherche sont tellement plus rapides que je ne pense pas que cela devrait être pris en compte. Encore une fois, si vous devez le rechercher, n'utilisez pas de tableau, utilisez un ensemble. (Ou un hachage.)

Plus le tableau est petit, plus les méthodes du tableau s'exécuteront rapidement, mais elles ne vont toujours pas suivre, bien que dans les petits tableaux, la différence puisse être minime.

« D' abord », « Moyen » et « Dernier » reflètent l'utilisation de first, size / 2et lastpour ARRAYl'élément recherchée. Cet élément sera utilisé lors de la recherche des variables ARRAYet SET.

Des modifications mineures ont été apportées aux méthodes comparées, > 0car le test devrait être >= 0destiné aux indextests de type.

Plus d'informations sur Fruity et sa méthodologie sont disponibles dans son README .

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.