ruby - through - wbr tag w3schools




Verifique se existe um valor em uma matriz no Ruby (15)

Aqui está mais uma maneira de fazer isso:

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

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

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

Como faço para verificar se existe na matriz sem fazer um loop? Existe uma maneira simples de verificar se o valor existe, nada mais?


Essa é outra maneira de fazer isso: use o método de índice Array #.

Retorna o índice da primeira ocorrência do elemento na matriz.

exemplo:

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

index () também pode ter um bloco

por exemplo

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

aqui, retorne o índice da primeira palavra na matriz que contém a letra 'o'.


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

Experimentar

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

Há o contrário, também!

Suponha que o array seja [: edit,: update,: create,: show] - bem, talvez todos os sete pecados mortais / repousantes :)

E ainda brincar com a idéia de puxar uma ação válida de alguma corda - digamos

meu irmão gostaria que eu atualizasse seu perfil

Solução

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

Isto irá dizer-lhe não só que existe, mas também quantas vezes aparece:

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

Que tal este caminho?

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

Ruby tem 11 métodos para encontrar elementos em uma matriz.

O preferido é include?

Ou para acesso repetido, criando um conjunto e, em seguida, chamando 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 true valor ish se o elemento estiver presente.

include? é o método preferido. Ele usa uma linguagem C for loop internamente que quebra quando um elemento corresponde às rb_equal_opt/rb_equal internas rb_equal_opt/rb_equal . Não pode ser muito mais eficiente, a menos que você crie um conjunto para verificações repetidas de associação.

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 classe Array e usa uma implementação não otimizada do módulo Enumerable que literalmente enumera através de 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 código Ruby isso faz 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? tem O(n) complexidade de tempo desde que ambos busquem o array pela primeira ocorrência do valor esperado.

Podemos usar um conjunto para obter O(1) tempo de acesso O(1) ao custo de criar primeiro uma representação hash do array. Se você verificar repetidamente a associação na mesma matriz, esse investimento inicial poderá ser recompensado rapidamente. Set não é implementado em C, mas como classe Ruby simples, ainda assim o tempo de acesso O(1) do @hash subjacente faz valer 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 apenas cria uma instância @hash interna, mapeia todos os objetos para true e verifica a associação usando Hash#include? que é implementado com O(1) tempo de acesso O(1) na classe Hash .

Não vou discutir os outros 7 métodos, pois todos são menos eficientes.

Na verdade, existem ainda mais métodos com complexidade de O(n) além dos 11 listados acima, mas decidi não listá-los, uma vez que varre todo o array ao invés de quebrar no primeiro jogo.

Não use isso,

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

Se você não quiser fazer um loop, não há como fazer isso com Arrays. Você deve usar um conjunto em vez disso.

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

Define trabalhar internamente como hashes, portanto, Ruby não precisa percorrer a coleção para encontrar itens, pois, como o nome sugere, ele gera hashes das chaves e cria um mapa de memória para que cada hash aponte para um determinado ponto na 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 os conjuntos e as chaves hash só podem incluir itens exclusivos e se você adicionar muitos itens, o Ruby precisará refazer a operação novamente após determinado número de itens para criar um novo mapa que atenda a um maior espaço de chaves. Para saber mais sobre isso, recomendo que você assista ao MountainWest RubyConf 2014 - Big O em um Hash Caseiro de Nathan Long.

Aqui está um benchmark:

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)

Se você precisar verificar múltiplos tempos para qualquer chave, converta arr para hash e agora verifique em 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 de Hash#has_key? versus include?

Parameter              Hash#has_key?                 Array#include 

Time Complexity         O(1) operation                O(n) operation 

Access Type             Accesses Hash[key] if it      Iterates through each element
                        returns any value then        of the array till it
                        true is returned to the       finds the value in Array
                        Hash#has_key? call
                        call    

Para verificação única usando o include? está bem


Se você tem em mente mais valores ... você pode tentar:

Exemplo: se Gato e Cão existirem 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: membro? e incluir? são os mesmos.

Isso pode fazer o trabalho em uma linha!


Use Enumerable#include :

a = %w/Cat Dog Bird/

a.include? 'Dog'

Ou, se um número de testes for feito, você pode se livrar do loop (que include? até include? Has) e ir de O (n) para O (1) com:

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

1. Espero que isso seja óbvio, mas evitar objeções: sim, para apenas algumas pesquisas, o Hash [] e as operações de transposição dominam o perfil e são cada um O (n) em si.


Você está procurando include? :

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

se você não quiser usar incluir? você pode primeiro envolver o elemento em uma matriz e, em seguida, verificar se o elemento agrupado é igual à interseção da matriz e do 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

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




arrays