Como verificar se existe um valor em uma matriz no Ruby

1315

Eu tenho um valor 'Dog'e uma matriz ['Cat', 'Dog', 'Bird'].

Como verifico se ele existe na matriz sem passar por ela? Existe uma maneira simples de verificar se o valor existe, nada mais?

user211662
fonte
5
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
o homem de lata

Respostas:

1946

Você está procurando include?:

>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true
Brian Campbell
fonte
73
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:

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

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:

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

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?...

Marc-André Lafortune
fonte
2
Embora o Ruby não inclua #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%3F
campeterson
166

Tentar

['Cat', 'Dog', 'Bird'].include?('Dog')
Schmitzelburger
fonte
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/Cat Dog Bird/

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.

DigitalRoss
fonte
49

Se você quiser verificar por um bloco, você pode tentar 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  

Consulte Enumerável para obter mais informações.

Minha inspiração veio de " avaliar se o array tem algum item em ruby "

furgão
fonte
1
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)) {
      case Qundef:
        if (rb_equal(e, item)) return Qtrue;
        break;
      case Qtrue:
        return Qtrue;
    }
  }
  return Qfalse;
}

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();
  }
  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;
}

Traduzido para o código Ruby, isso ocorre sobre o seguinte:

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

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.

Aqui está a implementação da 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

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
...
akuhn
fonte
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)) {
            return Qtrue;
        }
    }
    return Qfalse;
}

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?:

trie.include? 'bird' #=> true
trie.include? 'duck' #=> false
Boris Stitnicky
fonte
8
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:

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

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:

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

E os resultados:

      user     system      total        real
array  7.020000   0.000000   7.020000 (  7.031525)
set    0.010000   0.000000   0.010000 (  0.004816)
Kimmo Lehto
fonte
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'.

Zack Xu
fonte
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.

akuhn
fonte
Bom saber..!
Cary Swoveland
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  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false
sumit
fonte
6
Observe que Object#in?foi adicionado apenas ao Rails (ie ActiveSupport) v3.1 +. Não está disponível no Ruby principal.
10266 Tom
5

Se você precisar verificar múltiplos tempos para qualquer tecla, converta arrpara hashe, agora, verifique 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

Desempenho do hash # has_key? versus Array # include?

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    

Para verificação única, include?é bom usar

aqfaridi
fonte
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:

 a = ['Cat', 'Dog', 'Bird']
 a.count("Dog")
 #=> 1
user3245240
fonte
12
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.

davissp14
fonte
3
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

Ao invés de:

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

Nota: member?e include?são os mesmos.

Isso pode fazer o trabalho em uma linha!

Daniel Antonio Nuñez Carhuayo
fonte
3

Se queremos não usar include?isso também funciona:

['cat','dog','horse'].select{ |x| x == 'dog' }.any?
xlembouras
fonte
3
qualquer? também aceita blocos: ['gato', 'cachorro', 'cavalo']. {| x | x == 'cachorro'}
maikonas 6/11/2014
3
['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}
=> "Dog"
!['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}.nil?
=> true
Rahul Patel
fonte
['Cat', nil, 'Dog'].detect { |x| x == nil } #=> nil. Foi nilencontrado?
Cary Swoveland
2

Que tal esse caminho?

['Cat', 'Dog', 'Bird'].index('Dog')
ajahongir
fonte
Ainda vai percorrer a matriz apenas para encontrar o elemento. Então, ele deve retornar o índice desse elemento.
the Tin Man
2

Se você estiver tentando fazer isso em um teste de unidade MiniTest , poderá usar assert_includes. Exemplo:

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

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:

"my brother would like me to update his profile"

Então:

[ :edit, :update, :create, :show ].select{|v| v if "my brother would like me to update his profile".downcase =~ /[,|.| |]#{v.to_s}[,|.| |]/}
walt_die
fonte
1
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á /[,. ]/
correta
Como o @bkDJ diz, o regex está errado. rubular.com/r/4EG04rANz6KET6
the Tin Man
1

Se você deseja retornar o valor não apenas verdadeiro ou falso, use

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

Isso retornará 'Dog' se existir na lista, caso contrário, nulo.

gitb
fonte
Ou o uso 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.
Mahemoff 30/06/19
0

Aqui está mais uma maneira de fazer isso:

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

present = arr.size != (arr - [e]).size
Wand Maker
fonte
1
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.
Cary Swoveland
0
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")
Matthew Maurice
fonte
0

ele tem muitas maneiras de encontrar um elemento em qualquer matriz, mas a maneira mais simples é 'in?' método.

example:
arr = [1,2,3,4]
number = 1
puts "yes #{number} is present in arr" if number.in? arr
Abhishek kumar
fonte
2
Nota: Conforme descrito na presente resposta , o método in?exige ActiveSupporta ser importado: require active_support.
26419 Patrick Patrick
Não requer todo o ActiveSupport se você usar as extensões principais .
the Tin Man
0

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.

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

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:

# 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

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 .

do homem de lata
fonte