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
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:
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:
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?...
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
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/CatDogBird/
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.
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)){caseQundef:if(rb_equal(e, item))returnQtrue;break;caseQtrue:returnQtrue;}}returnQfalse;}
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();}returnQnil;}
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;}
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.
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)){returnQtrue;}}returnQfalse;}
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?:
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:
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 ".
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.
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 #=> true8.in? a #=> false
a.member?1#=> true
a.member?8#=> false
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:
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.
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
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/[,. ]/
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
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.
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é.
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.
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 .
hash = arr.map {|x| [x,true]}.to_h
, vérifiez maintenant sihash.has_key? 'Dog'
retourne vrai ou nonRéponses:
Vous recherchez
include?
:la source
%w(Cat Dog Bird).include? 'Dog'
#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.[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'
Il existe une
in?
méthode dansActiveSupport
(partie de Rails) depuis la v3.1, comme l'a souligné @campaterson. Donc dans Rails, ou si vousrequire 'active_support'
, vous pouvez écrire:OTOH, il n'y a aucun
in
opé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 tousEnumerable
s , y comprisArray
,Hash
,Set
,Range
: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 (ieO(1)
). Ainsi, si votre tableau est constant, par exemple, c'est une bonne idée d'utiliser un ensemble à la place. Par exemple:Un test rapide révèle que l'appel
include?
à un élément 10Set
est environ 3,5 fois plus rapide que l'appel à l'équivalentArray
(si l'élément n'est pas trouvé).Une dernière note de clôture: méfiez-vous lors de l'utilisation
include?
sur unRange
, il y a des subtilités, alors référez-vous au doc et comparez aveccover?
...la source
#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%3FEssayer
la source
Utilisation
Enumerable#include
: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: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.
la source
Si vous souhaitez vérifier par un bloc, vous pouvez essayer
any?
ouall?
.Voir Enumerable pour plus d'informations.
Mon inspiration est venue de " évaluer si le tableau contient des éléments en rubis "
la source
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 appelezinclude?
oumember?
.Voici tous:
Ils renvoient tous une
true
valeur ish si l'élément est présent.include?
est la méthode préférée. Il utilise unefor
boucle de langage C en interne qui se casse lorsqu'un élément correspond auxrb_equal_opt/rb_equal
fonctions 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.member?
n'est pas redéfini dans laArray
classe et utilise une implémentation non optimisée duEnumerable
module qui énumère littéralement tous les éléments:Traduit en code Ruby, cela permet:
Les deux
include?
etmember?
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.
Set
n'est pas implémenté en C mais en tant que classe Ruby simple, le temps d'accès O (1) du sous-jacent en@hash
vaut la peine.Voici l'implémentation de la classe Set:
Comme vous pouvez le voir, la classe Set crée simplement une
@hash
instance interne , mappe tous les objetstrue
et vérifie ensuite l'appartenance en utilisantHash#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:
la source
11
moyens que vous avez énumérés. Tout d'abord, vous pouvez difficilement compterindex
etfind_index
(oufind
etdetect
) 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> 0
sont incorrectes, ce qui, j'en suis sûr, était un oubli. (suite)arr.index(e)
, par exemple, renvoie0
ifarr[0] == e
. Vous vous souviendrez desarr.index(e)
retoursnil
s'ile
n'est pas présent.index
ne peut pas être utilisé, cependant, si l'on cherchenil
dansarr
. (Même problème avecrindex
, 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)arr.count(e) > 0
,arr != arr.dup.delete(e)
,arr != arr - [e]
etarr & [e] == [e]
. On pourrait également employerselect
etreject
.Plusieurs réponses suggèrent
Array#include?
, mais il y a une mise en garde importante: En regardant la source, mêmeArray#include?
effectuer une boucle: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-trie
dans cet exemple: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 queArray#include?
, en utilisant sublinéaireTrie#include?
:la source
a.each do ... end
Umm ...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).Set
utilise des hachages pour indexer ses membres, doncSet#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)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.
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:
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:
Et les résultats:
la source
include?
s'arrête pas au premier coup?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.Voici une autre façon de procéder: utilisez la
Array#index
méthode.Il renvoie l'index de la première occurrence de l'élément dans le tableau.
Par exemple:
index()
peut également prendre un bloc:Par exemple:
Cela renvoie l'index du premier mot du tableau qui contient la lettre «o».
la source
index
itère toujours sur le tableau, il retourne juste la valeur de l'élément.Fait amusant,
Vous pouvez utiliser
*
pour vérifier l'appartenance à un tableau dans unecase
expression.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
array
n'est pas réellement un tableau mais un seul élément, il correspondra à cet élément.la source
when
possible afin que d'autres vérifications plus rapides soient éliminées rapidement.Il existe plusieurs façons d'accomplir cela. Certains d'entre eux sont les suivants:
la source
Object#in?
n'a été ajouté qu'à Rails (ieActiveSupport
) v3.1 +. Il n'est pas disponible dans le noyau Ruby.Si vous avez besoin de vérifier plusieurs fois pour n'importe quelle clé, convertissez
arr
enhash
, et vérifiez maintenant O (1)Performances de Hash # has_key? contre Array # include?
Pour une vérification unique, l'utilisation
include?
est correctela source
Cela vous dira non seulement qu'il existe mais aussi combien de fois il apparaît:
la source
.any?
il reviendra dès qu'il trouvera le premier élément correspondant,.count
traitera toujours le tableau entier.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
.la source
<=>
, ce qui n'est pas toujours le cas. Supposons, par exemple, que les éléments du tableau soient des hachages.Tu peux essayer:
Exemple: si Cat et Dog existent dans le tableau:
Au lieu de:
Remarque:
member?
etinclude?
sont les mêmes.Cela peut faire le travail en une seule ligne!
la source
Si nous voulons ne pas utiliser
include?
cela fonctionne aussi:la source
la source
['Cat', nil, 'Dog'].detect { |x| x == nil } #=> nil
. A éténil
trouvé?Et de cette façon?
la source
Si vous essayez de le faire dans un test unitaire MiniTest , vous pouvez utiliser
assert_includes
. Exemple:la source
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:
Alors:
la source
/[,|.| |]#{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}(,|\.| |$)/
/[,. ]/
Si vous souhaitez renvoyer la valeur non seulement vrai ou faux, utilisez
Cela renverra «Dog» s'il existe dans la liste, sinon nul.
la source
array.any?{|x| x == 'Dog'}
si vous ne voulez vrai / faux (pas la valeur), mais souhaitez également comparer contre un bloc comme celui - ci.Voici une autre façon de procéder:
la source
arr != arr - [e]
.arr & [e] == [e]
est une autre voie dans le même sens.la source
la source
in?
doitActiveSupport
être importée:require active_support
.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é.la source
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:
Ce qui, lorsqu'il est exécuté sur mon ordinateur portable Mac OS, se traduit par:
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 / 2
etlast
pourARRAY
l'élément recherchée. Cet élément sera utilisé lors de la recherche des variablesARRAY
etSET
.Des modifications mineures ont été apportées aux méthodes comparées,
> 0
car le test devrait être>= 0
destiné auxindex
tests de type.Plus d'informations sur Fruity et sa méthodologie sont disponibles dans son README .
la source