use o .include? método . Retorna um booleano que é o que você deseja. No seu caso, basta digitar: ['Cat', 'Dog', 'Bird']. Include ('Dog') e deve retornar o valor booleano true.
Jwan622
não use incluir? método, se você deseja verificar várias vezes se há um valor diferente na matriz ou não porque inclui? cada vez que irá iterar sobre array tomada O (n) operação para procurar cada vez, em vez fazer um hash hash = arr.map {|x| [x,true]}.to_h, agora verificar se hash.has_key? 'Dog' retorna verdade ou não
aqfaridi
3
Você realmente não pode fazer isso totalmente "sem passar por ele". É logicamente impossível, o computador simplesmente não pode ter certeza se a matriz contém o elemento sem percorrer os elementos para verificar se algum deles é o que está procurando. A menos que esteja vazio, é claro. Então acho que você não precisa de um loop.
Tim M.
Veja os benchmarks abaixo para testes da diferença das várias maneiras de encontrar um elemento em uma matriz e um conjunto. stackoverflow.com/a/60404934/128421
Sintaxe alternativa:%w(Cat Dog Bird).include? 'Dog'
scarver2
184
Às vezes eu gostaria que fosse "contém" para não incluir. Eu sempre misturo com inclui.
Henley Chiu
19
Permitam-me apenas observar que, internamente, #include?ainda realiza loop. O codificador é salvo de escrever o loop explicitamente. Eu adicionei uma resposta que executa a tarefa verdadeiramente sem loop.
Boris Stitnicky
8
@HenleyChiu I que foi chamado[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'
4
@AlfonsoVergara Sim, qualquer solução para uma matriz deve executar algum tipo de loop internamente; não há como testar a associação de uma matriz sem fazer loop. Se você não deseja fazer nenhum loop, mesmo internamente, precisará usar uma estrutura de dados diferente, como uma tabela de hash perfeita com chaves de tamanho fixo. Dado que não há nenhuma maneira para testar a associação em uma matriz sem loop internamente, eu interpretei a questão para significar "sem ter que escrever o loop explicitamente mim mesmo"
Brian Campbell
250
Existe um in?método em ActiveSupport(parte do Rails) desde a v3.1, como apontado por @campaterson. Portanto, no Rails, ou se você require 'active_support', você pode escrever:
'Unicorn'.in?(['Cat','Dog','Bird'])# => false
OTOH, não há inoperador ou #in?método no próprio Ruby, mesmo que já tenha sido proposto anteriormente, em particular por Yusuke Endoh, um membro de alto nível do núcleo do rubi.
Como foi assinalado por outros, o método inverso include?existe, para todos os Enumerables incluindo Array, Hash, Set, Range:
Observe que, se você tiver muitos valores em sua matriz, todos eles serão verificados um após o outro (ou seja O(n)), enquanto a pesquisa de um hash será um tempo constante (ou seja O(1)). Portanto, se sua matriz é constante, por exemplo, é uma boa ideia usar um conjunto . Por exemplo:
Um teste rápido revela que chamar include?um elemento 10 Seté cerca de 3,5x mais rápido do que chamá-lo no equivalente Array(se o elemento não for encontrado).
Uma nota final: seja cauteloso ao usar include?a Range, existem sutilezas; consulte o documento e compare com cover?...
este é o mais velho de sintaxe, olhar ^^^ @ Brian resposta
jahrichie
62
@jahrichie, o que exatamente você considera "sintaxe mais antiga" nesta resposta, entre parênteses opcionais?
Dennis
3
eu concordo @ Dennis, isso não é mais antigo, os parênteses são opcionais e, na maioria dos casos, uma BOA PRÁTICA .... tente usar include sem parênteses em uma frase se uma linha, por exemplo, o que eu quis dizer é que, dependendo do seu caso, você deve ou deve usar colchetes ou não (não relacionadas com "velhos" sintaxes rubi em tudo)
d1jhoni1b
Essa é a única sintaxe que eu poderia trabalhar em uma operação ternária.
Michael Cameron
49
Use Enumerable#include:
a =%w/CatDogBird/
a.include?'Dog'
Ou, se vários testes forem realizados, 1 você poderá se livrar do loop (que ainda include?possui) e passar de O (n) para O (1) com:
h =Hash[[a, a].transpose]
h['Dog']
1. Espero que isso seja óbvio, mas evite objeções: sim, para apenas algumas pesquisas, as operações Hash [] e de transposição dominam o perfil e são cada O (n) em si.
Muito útil se você quiser verificar qualquer / todos aqueles corda está incluído em outra seqüência / constante
thanikkal
43
Ruby tem onze métodos para encontrar elementos em uma matriz.
O preferido é include?ou, para acesso repetido, crie um conjunto e depois chame include?ou member?.
Aqui estão todos eles:
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
Todos eles retornam um truevalor ish se o elemento estiver presente.
include?é o método preferido. Ele usa um forloop de linguagem C internamente que interrompe quando um elemento corresponde às rb_equal_opt/rb_equalfunções internas . Não pode ser muito mais eficiente, a menos que você crie um conjunto para verificações de associação repetidas.
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ão é redefinido na Arrayclasse e usa uma implementação não otimizada do Enumerablemódulo que literalmente enumera todos os elementos:
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;}
Traduzido para o código Ruby, isso ocorre sobre o seguinte:
Ambos include?e member?possuem complexidade de tempo O (n), pois os dois pesquisam na matriz a primeira ocorrência do valor esperado.
Podemos usar um conjunto para obter o tempo de acesso O (1) com o custo de ter que criar uma representação Hash da matriz primeiro. Se você verificar repetidamente a associação na mesma matriz, esse investimento inicial poderá render rapidamente. Setnão é implementado em C, mas como classe Ruby simples, ainda assim o tempo de acesso O (1) do subjacente @hashvale a pena.
Como você pode ver, a classe Set cria apenas uma @hashinstância interna , mapeia todos os objetos truee verifica a associação usando o Hash#include?que é implementado com o tempo de acesso O (1) na classe Hash.
Não discutirei os outros sete métodos, pois todos são menos eficientes.
Na verdade, existem ainda mais métodos com complexidade O (n) além dos 11 listados acima, mas decidi não listá-los, pois eles examinam toda a matriz em vez de interromperem na primeira correspondência.
Não use estes:
# bad examples
array.grep(element).any?
array.select {|each| each == element }.size >0...
Que descarado dizer que Ruby tem exatamente 11 maneiras de fazer qualquer coisa! Assim que você diz que alguém indicará que você perdeu o # 12, depois o 13 e assim por diante. Para argumentar, vou sugerir outras maneiras, mas primeiro deixe-me questionar as 11maneiras que você enumerou. Primeiro, você dificilmente pode contar indexe find_index(ou finde detect) como métodos separados, pois são apenas nomes diferentes para o mesmo método. Em segundo lugar, todas as expressões que terminam com > 0estão incorretas, o que eu tenho certeza que foi um descuido. (continuação)
Cary Swoveland
... arr.index(e), por exemplo, retorna 0se arr[0] == e. Você lembrará arr.index(e)retornos nilse enão estiver presente. indexnão pode ser usado, no entanto, se alguém está à procura de nilno arr. (Mesmo problema com o rindexque não está listado.). Converter a matriz em um conjunto e empregar métodos de conjunto é um pouco exagerado. Por que não converter para um hash (com chaves da matriz e valores arbitrários) e usar métodos de hash? Mesmo se a conversão para um conjunto estiver correta, existem outros métodos de conjunto que podem ser usados, como !arr.to_set.add?(e). (continuação)
Cary Swoveland
... Como prometido, aqui estão algumas mais métodos que poderiam ser usados: arr.count(e) > 0, arr != arr.dup.delete(e) , arr != arr - [e]e arr & [e] == [e]. Pode-se também empregar selecte reject.
Cary Swoveland
Eu recomendo o uso de um conjunto se o critério for que não sejam permitidas duplicatas e saber se existe um elemento específico na lista. Conjunto é muuuuito muito mais rápido. Matrizes realmente não devem ser usadas quando a pesquisa é necessária; Eles são mais usados como uma fila onde as coisas são armazenadas temporariamente para serem processadas em ordem. Hash e Set são melhores para verificar se existe algo.
the Tin Man
30
Várias respostas sugerem Array#include?, mas há uma ressalva importante: Olhando para a fonte, até Array#include?realiza loop:
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;}
A maneira de testar a presença palavra sem looping é através da construção de um trie para sua matriz. Existem muitas implementações disponíveis no mercado (google "ruby trie"). Vou usar rambling-trieneste exemplo:
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}
E agora estamos prontos para testar a presença de várias palavras em sua matriz sem repetir, com o O(log n)tempo, a mesma simplicidade sintática que Array#include?usando sublinear Trie#include?:
a.each do ... endUmm ... não sei como isso não é um loop
tckmn
27
Observe que isso realmente inclui um loop; qualquer coisa que não seja O (1) inclui algum tipo de loop. Acontece que é um loop sobre os caracteres da string de entrada. Observe também que uma resposta já mencionada Set#include?para pessoas preocupadas com eficiência; juntamente com o uso de símbolos em vez de cadeias, pode ser o caso médio de O (1) (se você usar cadeias, então apenas calculando o hash é O (n) onde n é o comprimento da cadeia). Ou, se você quiser usar bibliotecas de terceiros, poderá usar um hash perfeito, que é o pior caso de O (1).
Brian Campbell
4
O AFAIK Setusa hashes para indexar seus membros; portanto, Set#include?deve ter complexidade O (1) para um bem distribuído Set(mais especificamente O (tamanho da entrada) para o hash e O (log (n / número de bucket)) para a busca)
Uri Agassi
11
O custo de criar e manter o trie é o mesmo. Se você estiver executando muitas operações de pesquisa na matriz, vale a pena o tempo de memória e tempo de preencher e tentar mantê-lo, mas para verificações únicas ou mesmo centenas ou milhares de verificações, O (n) é perfeitamente adequado. Outra opção que não requer adição de dependências seria classificar a matriz ou mantê-la em ordem classificada; nesse caso, uma operação de pesquisa binária O (lg n) pode ser usada para verificar a inclusão.
speakingcode
1
@speakingcode, você pode estar certo do ponto de vista pragmático. Mas o OP pede para "verificar se o valor existe, nada mais, sem loop". Quando escrevi essa resposta, havia muitas soluções pragmáticas aqui, mas nenhuma que realmente atendesse aos requisitos literais do solicitante. Sua observação de que as BSTs estão relacionadas a tentativas está correta, mas, para strings, trie é a ferramenta certa para o trabalho, até a Wikipedia sabe disso . A complexidade de construir e manter um negócio bem implementado é surpreendentemente favorável.
Boris Stitnicky
18
Se você não deseja fazer um loop, não há como fazê-lo com matrizes. Você deve usar um conjunto.
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
Os conjuntos funcionam internamente como Hashes, portanto, o Ruby não precisa percorrer a coleção para encontrar itens, pois, como o nome indica, gera hashes das chaves e cria um mapa de memória para que cada hash aponte para um determinado ponto da memória. O exemplo anterior feito com um Hash:
A desvantagem é que as chaves Sets e Hash podem incluir apenas itens exclusivos e, se você adicionar muitos itens, Ruby terá que refazer a coisa toda após certo número de itens para criar um novo mapa que se adapte a um espaço de teclas maior. Para saber mais sobre isso, recomendo que você assista " MountainWest RubyConf 2014 - Big O em um Hash Caseiro de Nathan Long ".
Se você usar o detect, poderá pelo menos reduzir o loop. detect irá parar no primeiro item 'detectado' (o bloco passado para o item é avaliado como verdadeiro). Além disso, você pode dizer para detectar o que fazer se nada for detectado (você pode passar em um lambda).
aenw 23/07
1
@aenw não include?para no primeiro hit?
Kimmo Lehto
você está absolutamente correto. Estou tão acostumado a usar o detect que esqueci que incluir. obrigado pelo seu comentário - garantiu que eu atualizei meus conhecimentos.
aenw
include?pára na primeira ocorrência, mas se essa ocorrência estiver no final da lista ... Qualquer solução que dependa de uma matriz para armazenamento terá um desempenho degradante à medida que a lista aumenta, especialmente quando é necessário localizar um elemento no final da lista. Lista. Hash e Set não têm esse problema, nem uma lista ordenada e uma pesquisa binária.
the Tin Man
Isso é basicamente o que foi essa resposta em primeiro lugar :)
Kimmo Lehto
17
Esta é outra maneira de fazer isso: use o Array#indexmétodo
Retorna o índice da primeira ocorrência do elemento na matriz.
Por exemplo:
a =['cat','dog','horse']if a.index('dog')
puts "dog exists in the array"end
index() também pode pegar um bloco:
Por exemplo:
a =['cat','dog','horse']
puts a.index {|x| x.match /o/}
Isso retorna o índice da primeira palavra na matriz que contém a letra 'o'.
indexainda itera sobre a matriz, apenas retorna o valor do elemento
the Tin Man
13
Fato engraçado,
Você pode usar *para verificar a associação da matriz em uma caseexpressão.
case element
when*array
...else...end
Observe o pouco *na cláusula when, isso verifica a associação na matriz.
Todo o comportamento mágico usual do operador splat se aplica; por exemplo, se arraynão for realmente uma matriz, mas um único elemento, ele corresponderá a esse elemento.
Também seria uma primeira verificação lenta em uma declaração de caso, então eu a usaria na última whenpossível para que outras verificações mais rápidas sejam eliminadas rapidamente.
o homem de lata
9
Existem várias maneiras de conseguir isso. Alguns deles são os seguintes:
a =[1,2,3,4,5]2.in? a #=> true8.in? a #=> false
a.member?1#=> true
a.member?8#=> false
Parâmetro Hash # has_key? A matriz # inclui
Complexidade de tempo O (1) operação O (n) operação
Tipo de acesso Acessa Hash [chave] se iterar através de cada elemento
retorna qualquer valor então da matriz até
true é retornado para localiza o valor na matriz
Hash # has_key? ligar
ligar
Você ainda não está percorrendo a matriz para convertê-la em um hash?
chrisjacob
2
Sim, eu fiz, mas agora tenho resposta pré-computada para verificar se existe algum elemento na matriz ou não. Agora, da próxima vez que eu procurar por outra palavra, será necessário O (1) para consulta, embora o pré-cálculo tenha O (n). Na verdade eu usei incluir? no meu aplicativo loop interno para verificar se determinado existir elemento na matriz ou não, isso estraga o desempenho, ele check-in ruby-prof, que era o gargalo
aqfaridi
1
.... mas O (n) espaço e também, não, é O (n) tempo, porque como @ chris72205 corretamente aponta, você precisa iterar primeiro sua matriz. O (1) + O (n) = O (n). Então, de fato, isso é muito pior do que incluir?
Rambatino 2/06
Cara, eu estava dizendo que não use o loop interno do include, para verificação única usando o include é bom. Portanto, leia primeiro o caso de uso, é melhor se você precisar verificar várias vezes o loop interno.
aqfaridi
A conversão de uma matriz em um hash é cara, mas o uso de uma matriz para pesquisar é a estrutura errada. As matrizes são melhores como filas em que a ordem pode ser importante; Os hashes e conjuntos são melhores quando a inclusão / existência / exclusividade é importante. Comece com o recipiente certo e o problema é discutível.
the Tin Man
4
Isso informará não apenas a existência, mas também quantas vezes ela será exibida:
Não faz sentido usá-lo, a menos que você queira saber quantas vezes ele aparece, porém, pois .any?retornará assim que encontrar o primeiro elemento correspondente, .countsempre processará toda a matriz.
Zaz
Embora isso tecnicamente diga se existe algo, também NÃO é a maneira certa de fazê-lo se você se preocupa com velocidade.
the Tin Man
4
Pelo que vale, os documentos do Ruby são um recurso incrível para esse tipo de pergunta.
Eu também anotaria o comprimento da matriz que você está pesquisando. O include?método executará uma pesquisa linear com complexidade O (n) que pode ficar muito feia dependendo do tamanho da matriz.
Se você estiver trabalhando com uma matriz grande (classificada), consideraria escrever um algoritmo de pesquisa binária que não deve ser muito difícil e tem o pior caso de O (log n).
Ou, se você estiver usando Ruby 2.0, poderá aproveitar bsearch.
Uma pesquisa binária assume que a matriz é classificada (ou ordenada de alguma forma), o que pode ser dispendioso para matrizes grandes, muitas vezes negando a vantagem.
the Tin Man
Uma pesquisa binária também exige que todos os pares de elementos sejam comparáveis <=>, o que nem sempre é o caso. Suponha, por exemplo, que os elementos da matriz sejam hashes.
Cary Swoveland
1
@CarySwoveland, deveria estar mais ou menos implícito que os elementos em uma matriz classificada são comparáveis.
Davissp14
4
Podes tentar:
Exemplo: se Gato e Cachorro existem na matriz:
(['Cat','Dog','Bird']&['Cat','Dog']).size ==2#or replace 2 with ['Cat','Dog].size
Sua expressão regular /[,|.| |]#{v.to_s}[,|.| |]/me faz pensar que você queria encontrar 'o nome da ação cercado por um de: vírgula, ponto, espaço ou nada', mas existem alguns erros sutis. "|update|"voltaria [:update]e "update"voltaria []. Classes de caracteres ( [...]) não usam pipes ( |) para separar caracteres. Mesmo se os mudarmos para groups ( (...)), você não poderá corresponder um caractere vazio. Assim, a regex provavelmente você queria é/(,|\.| |^)#{v.to_s}(,|\.| |$)/
bkDJ
o regex está ok (verificado em rubular.com) - e @Rambatino: o porquê seria como o Amazon Echo, por exemplo;) Você poderia dizer: "adicione frango à minha lista de compras" (e - bem - então você precisa adicione o: add para a matriz, mas eu acho que você começa a essência dele;)
walt_die
1
"o regex está ok (verificado em rubular.com)". Não está bem Sua regex não corresponderá a uma palavra-chave no início ou no final de uma string (por exemplo, "atualizar o perfil do meu irmão"). Se você não deseja corresponder ao início ou ao fim, sua regex ainda não está /[,. ]/
Esta é uma maneira terrivelmente ineficiente de fazer isso! Não estou com voto negativo porque não é tecnicamente incorreto e alguém pode aprender algo sobre Ruby lendo isso, mas há muitas respostas melhores acima.
Jibberia #
Conehead, você pode simplificar para arr != arr - [e]. arr & [e] == [e]é outra maneira na mesma linha.
Cary Swoveland
@CarySwoveland Não tirar sarro de chapéu de um feiticeiro ;-)
Wand Criador
Eu estava me referindo à cabeça do mago, não ao chapéu, com o maior respeito.
se você não quiser usar, include?pode agrupar o elemento em uma matriz e depois verificar se o elemento agrupado é igual à interseção da matriz e o elemento agrupado. Isso retornará um valor booleano com base na igualdade.
Eu sempre acho interessante executar alguns benchmarks para ver a velocidade relativa das várias maneiras de fazer alguma coisa.
A localização de um elemento da matriz no início, no meio ou no final afetará todas as pesquisas lineares, mas apenas afetará uma pesquisa em um conjunto.
A conversão de uma matriz em um conjunto causará um impacto no tempo de processamento; portanto, crie o conjunto a partir de uma matriz uma vez ou comece com um conjunto desde o início.
Que, quando executado no meu laptop Mac OS, resulta em:
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
Basicamente, os resultados me dizem para usar um conjunto para tudo, se vou procurar inclusão, a menos que eu possa garantir que o primeiro elemento seja o que eu quero, o que não é muito provável. Há alguma sobrecarga ao inserir elementos em um hash, mas os tempos de pesquisa são muito mais rápidos que acho que isso nunca deve ser considerado. Novamente, se você precisar procurá-lo, não use uma matriz, use um conjunto. (Ou um Hash.)
Quanto menor o Array, mais rápido os métodos Array serão executados, mas eles ainda não acompanharão, embora em pequenos arrays a diferença possa ser pequena.
"Primeiro", "Middle" e "Last" refletem o uso de first, size / 2e lastpara ARRAYpara o elemento que está sendo procurado. Esse elemento será usado ao pesquisar o ARRAYe SETvariáveis.
Pequenas alterações foram feitas nos métodos comparados > 0porque o teste deveria ser >= 0para indextestes de tipo.
Mais informações sobre o Fruity e sua metodologia estão disponíveis no README .
hash = arr.map {|x| [x,true]}.to_h
, agora verificar sehash.has_key? 'Dog'
retorna verdade ou nãoRespostas:
Você está procurando
include?
:fonte
%w(Cat Dog Bird).include? 'Dog'
#include?
ainda realiza loop. O codificador é salvo de escrever o loop explicitamente. Eu adicionei uma resposta que executa a tarefa verdadeiramente sem loop.[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'
Existe um
in?
método emActiveSupport
(parte do Rails) desde a v3.1, como apontado por @campaterson. Portanto, no Rails, ou se vocêrequire 'active_support'
, você pode escrever:OTOH, não há
in
operador ou#in?
método no próprio Ruby, mesmo que já tenha sido proposto anteriormente, em particular por Yusuke Endoh, um membro de alto nível do núcleo do rubi.Como foi assinalado por outros, o método inverso
include?
existe, para todos osEnumerable
s incluindoArray
,Hash
,Set
,Range
:Observe que, se você tiver muitos valores em sua matriz, todos eles serão verificados um após o outro (ou seja
O(n)
), enquanto a pesquisa de um hash será um tempo constante (ou sejaO(1)
). Portanto, se sua matriz é constante, por exemplo, é uma boa ideia usar um conjunto . Por exemplo:Um teste rápido revela que chamar
include?
um elemento 10Set
é cerca de 3,5x mais rápido do que chamá-lo no equivalenteArray
(se o elemento não for encontrado).Uma nota final: seja cauteloso ao usar
include?
aRange
, existem sutilezas; consulte o documento e compare comcover?
...fonte
#in?
em seu núcleo, se você estiver usando o Rails, ele estará disponível. api.rubyonrails.org/classes/Object.html#method-i-in-3F (Eu sei que essa é uma pergunta sobre Ruby, não sobre Rails, mas pode ajudar qualquer pessoa que queira usar#in?
no Rails. Parece que ele foi adicionado ao Rails. 3.1 apidock.com/rails/Object/in%3FTentar
fonte
Use
Enumerable#include
:Ou, se vários testes forem realizados, 1 você poderá se livrar do loop (que ainda
include?
possui) e passar de O (n) para O (1) com:1. Espero que isso seja óbvio, mas evite objeções: sim, para apenas algumas pesquisas, as operações Hash [] e de transposição dominam o perfil e são cada O (n) em si.
fonte
Se você quiser verificar por um bloco, você pode tentar
any?
ouall?
.Consulte Enumerável para obter mais informações.
Minha inspiração veio de " avaliar se o array tem algum item em ruby "
fonte
Ruby tem onze métodos para encontrar elementos em uma matriz.
O preferido é
include?
ou, para acesso repetido, crie um conjunto e depois chameinclude?
oumember?
.Aqui estão todos eles:
Todos eles retornam um
true
valor ish se o elemento estiver presente.include?
é o método preferido. Ele usa umfor
loop de linguagem C internamente que interrompe quando um elemento corresponde àsrb_equal_opt/rb_equal
funções internas . Não pode ser muito mais eficiente, a menos que você crie um conjunto para verificações de associação repetidas.member?
não é redefinido naArray
classe e usa uma implementação não otimizada doEnumerable
módulo que literalmente enumera todos os elementos:Traduzido para o código Ruby, isso ocorre sobre o seguinte:
Ambos
include?
emember?
possuem complexidade de tempo O (n), pois os dois pesquisam na matriz a primeira ocorrência do valor esperado.Podemos usar um conjunto para obter o tempo de acesso O (1) com o custo de ter que criar uma representação Hash da matriz primeiro. Se você verificar repetidamente a associação na mesma matriz, esse investimento inicial poderá render rapidamente.
Set
não é implementado em C, mas como classe Ruby simples, ainda assim o tempo de acesso O (1) do subjacente@hash
vale a pena.Aqui está a implementação da classe Set:
Como você pode ver, a classe Set cria apenas uma
@hash
instância interna , mapeia todos os objetostrue
e verifica a associação usando oHash#include?
que é implementado com o tempo de acesso O (1) na classe Hash.Não discutirei os outros sete métodos, pois todos são menos eficientes.
Na verdade, existem ainda mais métodos com complexidade O (n) além dos 11 listados acima, mas decidi não listá-los, pois eles examinam toda a matriz em vez de interromperem na primeira correspondência.
Não use estes:
fonte
11
maneiras que você enumerou. Primeiro, você dificilmente pode contarindex
efind_index
(oufind
edetect
) como métodos separados, pois são apenas nomes diferentes para o mesmo método. Em segundo lugar, todas as expressões que terminam com> 0
estão incorretas, o que eu tenho certeza que foi um descuido. (continuação)arr.index(e)
, por exemplo, retorna0
searr[0] == e
. Você lembraráarr.index(e)
retornosnil
see
não estiver presente.index
não pode ser usado, no entanto, se alguém está à procura denil
noarr
. (Mesmo problema com orindex
que não está listado.). Converter a matriz em um conjunto e empregar métodos de conjunto é um pouco exagerado. Por que não converter para um hash (com chaves da matriz e valores arbitrários) e usar métodos de hash? Mesmo se a conversão para um conjunto estiver correta, existem outros métodos de conjunto que podem ser usados, como!arr.to_set.add?(e)
. (continuação)arr.count(e) > 0
,arr != arr.dup.delete(e)
,arr != arr - [e]
earr & [e] == [e]
. Pode-se também empregarselect
ereject
.Várias respostas sugerem
Array#include?
, mas há uma ressalva importante: Olhando para a fonte, atéArray#include?
realiza loop:A maneira de testar a presença palavra sem looping é através da construção de um trie para sua matriz. Existem muitas implementações disponíveis no mercado (google "ruby trie"). Vou usar
rambling-trie
neste exemplo:E agora estamos prontos para testar a presença de várias palavras em sua matriz sem repetir, com o
O(log n)
tempo, a mesma simplicidade sintática queArray#include?
usando sublinearTrie#include?
:fonte
a.each do ... end
Umm ... não sei como isso não é um loopSet#include?
para pessoas preocupadas com eficiência; juntamente com o uso de símbolos em vez de cadeias, pode ser o caso médio de O (1) (se você usar cadeias, então apenas calculando o hash é O (n) onde n é o comprimento da cadeia). Ou, se você quiser usar bibliotecas de terceiros, poderá usar um hash perfeito, que é o pior caso de O (1).Set
usa hashes para indexar seus membros; portanto,Set#include?
deve ter complexidade O (1) para um bem distribuídoSet
(mais especificamente O (tamanho da entrada) para o hash e O (log (n / número de bucket)) para a busca)Se você não deseja fazer um loop, não há como fazê-lo com matrizes. Você deve usar um conjunto.
Os conjuntos funcionam internamente como Hashes, portanto, o Ruby não precisa percorrer a coleção para encontrar itens, pois, como o nome indica, gera hashes das chaves e cria um mapa de memória para que cada hash aponte para um determinado ponto da memória. O exemplo anterior feito com um Hash:
A desvantagem é que as chaves Sets e Hash podem incluir apenas itens exclusivos e, se você adicionar muitos itens, Ruby terá que refazer a coisa toda após certo número de itens para criar um novo mapa que se adapte a um espaço de teclas maior. Para saber mais sobre isso, recomendo que você assista " MountainWest RubyConf 2014 - Big O em um Hash Caseiro de Nathan Long ".
Aqui está uma referência:
E os resultados:
fonte
include?
para no primeiro hit?include?
pára na primeira ocorrência, mas se essa ocorrência estiver no final da lista ... Qualquer solução que dependa de uma matriz para armazenamento terá um desempenho degradante à medida que a lista aumenta, especialmente quando é necessário localizar um elemento no final da lista. Lista. Hash e Set não têm esse problema, nem uma lista ordenada e uma pesquisa binária.Esta é outra maneira de fazer isso: use o
Array#index
métodoRetorna o índice da primeira ocorrência do elemento na matriz.
Por exemplo:
index()
também pode pegar um bloco:Por exemplo:
Isso retorna o índice da primeira palavra na matriz que contém a letra 'o'.
fonte
index
ainda itera sobre a matriz, apenas retorna o valor do elementoFato engraçado,
Você pode usar
*
para verificar a associação da matriz em umacase
expressão.Observe o pouco
*
na cláusula when, isso verifica a associação na matriz.Todo o comportamento mágico usual do operador splat se aplica; por exemplo, se
array
não for realmente uma matriz, mas um único elemento, ele corresponderá a esse elemento.fonte
when
possível para que outras verificações mais rápidas sejam eliminadas rapidamente.Existem várias maneiras de conseguir isso. Alguns deles são os seguintes:
fonte
Object#in?
foi adicionado apenas ao Rails (ieActiveSupport
) v3.1 +. Não está disponível no Ruby principal.Se você precisar verificar múltiplos tempos para qualquer tecla, converta
arr
parahash
e, agora, verifique O (1)Desempenho do hash # has_key? versus Array # include?
Para verificação única,
include?
é bom usarfonte
Isso informará não apenas a existência, mas também quantas vezes ela será exibida:
fonte
.any?
retornará assim que encontrar o primeiro elemento correspondente,.count
sempre processará toda a matriz.Pelo que vale, os documentos do Ruby são um recurso incrível para esse tipo de pergunta.
Eu também anotaria o comprimento da matriz que você está pesquisando. O
include?
método executará uma pesquisa linear com complexidade O (n) que pode ficar muito feia dependendo do tamanho da matriz.Se você estiver trabalhando com uma matriz grande (classificada), consideraria escrever um algoritmo de pesquisa binária que não deve ser muito difícil e tem o pior caso de O (log n).
Ou, se você estiver usando Ruby 2.0, poderá aproveitar
bsearch
.fonte
<=>
, o que nem sempre é o caso. Suponha, por exemplo, que os elementos da matriz sejam hashes.Podes tentar:
Exemplo: se Gato e Cachorro existem na matriz:
Ao invés de:
Nota:
member?
einclude?
são os mesmos.Isso pode fazer o trabalho em uma linha!
fonte
Se queremos não usar
include?
isso também funciona:fonte
fonte
['Cat', nil, 'Dog'].detect { |x| x == nil } #=> nil
. Foinil
encontrado?Que tal esse caminho?
fonte
Se você estiver tentando fazer isso em um teste de unidade MiniTest , poderá usar
assert_includes
. Exemplo:fonte
Existe o contrário.
Suponha que a matriz seja
[ :edit, :update, :create, :show ]
, bem, talvez os sete pecados mortais / repousantes .E brinque com a idéia de puxar uma ação válida de alguma string:
Então:
fonte
/[,|.| |]#{v.to_s}[,|.| |]/
me faz pensar que você queria encontrar 'o nome da ação cercado por um de: vírgula, ponto, espaço ou nada', mas existem alguns erros sutis."|update|"
voltaria[:update]
e"update"
voltaria[]
. Classes de caracteres ([...]
) não usam pipes (|
) para separar caracteres. Mesmo se os mudarmos para groups ((...)
), você não poderá corresponder um caractere vazio. Assim, a regex provavelmente você queria é/(,|\.| |^)#{v.to_s}(,|\.| |$)/
/[,. ]/
Se você deseja retornar o valor não apenas verdadeiro ou falso, use
Isso retornará 'Dog' se existir na lista, caso contrário, nulo.
fonte
array.any?{|x| x == 'Dog'}
se você não quer true / false (não o valor), mas também querem comparar com um bloco como este.Aqui está mais uma maneira de fazer isso:
fonte
arr != arr - [e]
.arr & [e] == [e]
é outra maneira na mesma linha.fonte
fonte
in?
exigeActiveSupport
a ser importado:require active_support
.se você não quiser usar,
include?
pode agrupar o elemento em uma matriz e depois verificar se o elemento agrupado é igual à interseção da matriz e o elemento agrupado. Isso retornará um valor booleano com base na igualdade.fonte
Eu sempre acho interessante executar alguns benchmarks para ver a velocidade relativa das várias maneiras de fazer alguma coisa.
A localização de um elemento da matriz no início, no meio ou no final afetará todas as pesquisas lineares, mas apenas afetará uma pesquisa em um conjunto.
A conversão de uma matriz em um conjunto causará um impacto no tempo de processamento; portanto, crie o conjunto a partir de uma matriz uma vez ou comece com um conjunto desde o início.
Aqui está o código de referência:
Que, quando executado no meu laptop Mac OS, resulta em:
Basicamente, os resultados me dizem para usar um conjunto para tudo, se vou procurar inclusão, a menos que eu possa garantir que o primeiro elemento seja o que eu quero, o que não é muito provável. Há alguma sobrecarga ao inserir elementos em um hash, mas os tempos de pesquisa são muito mais rápidos que acho que isso nunca deve ser considerado. Novamente, se você precisar procurá-lo, não use uma matriz, use um conjunto. (Ou um Hash.)
Quanto menor o Array, mais rápido os métodos Array serão executados, mas eles ainda não acompanharão, embora em pequenos arrays a diferença possa ser pequena.
"Primeiro", "Middle" e "Last" refletem o uso de
first
,size / 2
elast
paraARRAY
para o elemento que está sendo procurado. Esse elemento será usado ao pesquisar oARRAY
eSET
variáveis.Pequenas alterações foram feitas nos métodos comparados
> 0
porque o teste deveria ser>= 0
paraindex
testes de tipo.Mais informações sobre o Fruity e sua metodologia estão disponíveis no README .
fonte