bidimensionales arreglos array ruby arrays

arreglos - array ruby



Compruebe si existe un valor en una matriz en Ruby (22)

¡También hay al revés!

Supongamos que la matriz es [: editar,: actualizar,: crear,: mostrar] - bueno, tal vez los siete pecados mortales / reparadores completos :)

Y más juguete con la idea de sacar una acción válida de una cuerda, por ejemplo

A mi hermano le gustaría que actualice su perfil.

Solución

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

Tengo un valor ''Dog'' y una matriz [''Cat'', ''Dog'', ''Bird''] .

¿Cómo verifico si existe en la matriz sin pasar por ella? ¿Hay una forma sencilla de comprobar si el valor existe, nada más?


¿Estás buscando include? :

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


¿Qué tal de esta manera?

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


Aquí hay una manera más de hacer esto:

arr = [''Cat'', ''Dog'', ''Bird''] e = ''Dog'' present = arr.size != (arr - [e]).size


El uso de Enumerable#include :

a = %w/Cat Dog Bird/ a.include? ''Dog''

O, si se realizan varias pruebas, 1 puede deshacerse del bucle (que incluso include? Ha) y pasar de O (n) a O (1) con:

h = Hash[[a, a].transpose] h[''Dog''] 1. Espero que esto sea obvio, pero para evitar objeciones: sí, solo para algunas búsquedas, el Hash [] y las operaciones de transposición dominan el perfil y son cada uno O (n) .


Esta es otra forma de hacer esto: use el método de índice Array #.

Devuelve el índice de la primera aparición del elemento en la matriz.

ejemplo:

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

index () tambien puede tomar un bloque

por ejemplo

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

aquí, devuelva el índice de la primera palabra de la matriz que contiene la letra ''o''.


Esto le dirá no solo que existe, sino también cuántas veces aparece:

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


Hay múltiples maneras de lograr esto. Algunos de ellos son los siguientes:

a = [1,2,3,4,5] 2.in? a #=> true 8.in? a #=> false a.member? 1 #=> true a.member? 8 #=> false


Hay un in? Método en ActiveSupport (parte de Rails) desde v3.1, como lo señaló @campaterson. Entonces, dentro de Rails, o si require ''active_support'' , puede escribir:

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

OTOH, no hay in operador o #in? Método en el propio Ruby, a pesar de que se ha propuesto anteriormente, en particular por Yusuke Endoh, un miembro de primera categoría de ruby-core.

Como han señalado otros, el método inverso include? existe, para todos los Enumerable s incluyendo Array , Hash , Set , Range :

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

Tenga en cuenta que si tiene muchos valores en su matriz, todos se verán uno tras otro (es decir, O(n) ), mientras que la búsqueda de un hash será constante (es decir, O(1) ). Por lo tanto, si la matriz es constante, por ejemplo, es una buena idea usar un Set lugar. P.ej:

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

Una prueba rápida revela que las llamadas include? en un conjunto de 10 elementos es aproximadamente 3.5x más rápido que llamarlo en la Array equivalente (si no se encuentra el elemento).

Una nota final de cierre: ser cauteloso al usar include? en un Range , hay sutilezas, así que consulte el documento y compare con la cover? ...


Hecho de la diversión,

Puede usar * para verificar la pertenencia a una matriz en las expresiones de un case .

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

Observe el poco * en la cláusula when, esto verifica la pertenencia a la matriz.

Se aplica todo el comportamiento mágico habitual del operador splat, por ejemplo, si la array no es realmente una matriz sino un elemento único, coincidirá con ese elemento.


Para lo que vale la pena, los documentos de Ruby son un recurso increíble para este tipo de preguntas.

También tomaría nota de la longitud de la matriz que está buscando. El include? El método ejecutará una búsqueda lineal con O (n) complejidad que puede ser bastante desagradable dependiendo del tamaño de la matriz.

Si está trabajando con una matriz grande (ordenada), consideraría escribir un algoritmo de búsqueda binario que no debería ser demasiado difícil y tiene el peor de los casos de O (log n).

O si está utilizando Ruby 2.0, puede aprovechar bsearch .


Ruby tiene 11 métodos para encontrar elementos en una matriz.

El preferido es include?

¿O para acceso repetido, creando un conjunto y luego llamando a include? o member?

Aquí están todos ellos,

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 ellos devuelven un true valor ish si el elemento está presente.

include? Es el método preferido. Utiliza un lenguaje C for bucle interno que se rompe cuando un elemento coincide con las rb_equal_opt/rb_equal internas rb_equal_opt/rb_equal . No puede ser mucho más eficiente a menos que cree un conjunto para verificaciones de membresía 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? no se redefine en la clase Array y utiliza una implementación no optimizada del módulo Enumerable que enumera literalmente a través de todos los 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; }

Traducido al código Ruby esto hace acerca de lo siguiente

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? y member? tiene complejidad de tiempo O(n) ya que ambos buscan en la matriz la primera aparición del valor esperado.

Podemos usar un conjunto para obtener el tiempo de acceso O(1) al costo de tener que crear primero una representación hash de la matriz. Si verifica repetidamente la membresía en la misma matriz, esta inversión inicial puede amortizarse rápidamente. Set no se implementa en C, pero como clase Ruby simple, aún así el tiempo de acceso O(1) del @hash subyacente hace que valga la pena.

Aquí está la implementación de la clase 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 puede ver, la clase Set solo crea una instancia interna de @hash , asigna todos los objetos a true y luego verifica la membresía usando Hash#include? que se implementa con O(1) tiempo de acceso en la clase Hash .

No discutiré los otros 7 métodos ya que todos son menos eficientes.

En realidad, hay aún más métodos con O(n) complejidad más allá de los 11 enumerados anteriormente, pero decidí no incluirlos en la lista ya que escanear la matriz completa en lugar de romper en la primera coincidencia.

No uses estos,

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


Si necesita verificar varias veces para cualquier clave, convierta arr a hash , y ahora marque 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

Rendimiento 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 una sola verificación de tiempo usando include? está bien


Si no quieres hacer un bucle, no hay forma de hacerlo con Arrays. Deberías usar un Set en su lugar.

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

Los conjuntos funcionan internamente como hashes, por lo que Ruby no necesita recorrer la colección para encontrar elementos, ya que como su nombre lo indica, genera hashes de las claves y crea un mapa de memoria para que cada hash señale un punto determinado en la memoria. El ejemplo anterior hecho con un hash:

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

El inconveniente es que los conjuntos y las claves de hash solo pueden incluir elementos únicos y, si agrega muchos elementos, Ruby tendrá que volver a realizar todo el proceso después de cierto número de elementos para crear un nuevo mapa que se adapte a un mayor espacio de teclas. Para más información sobre esto, te recomiendo que veas MountainWest RubyConf 2014 - Big O en un Hash casero por Nathan Long

Aquí hay un punto de referencia:

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

Y los resultados:

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


Si no quieres usar incluir? primero puede envolver el elemento en una matriz y luego verificar si el elemento envuelto es igual a la intersección de la matriz y el elemento envuelto. Esto devolverá un valor booleano basado en la igualdad.

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


Si queremos no utilizar include? esto también funciona:

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


Si quieres comprobar por un bloque, puedes probar alguno? ¿O todo?

%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

Los detalles están aquí: http://ruby-doc.org/core-1.9.3/Enumerable.html
Mi inspiración viene de aquí: https://.com/a/10342734/576497


Si tienes en mente más valores ... puedes probar:

Ejemplo: si Cat and Dog existe en el array:

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

En lugar de:

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

Nota: miembro? e incluir? son lo mismo.

¡Esto puede hacer el trabajo en una sola línea!


Tratar

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


Varias respuestas sugieren Array#include? , pero hay una advertencia importante: mirando la fuente, ¿incluso Array#include? realiza bucles:

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

La forma de probar la presencia de la palabra sin hacer un bucle es mediante la construcción de un trie para su matriz. Hay muchas implementaciones de trie por ahí (google "ruby trie"). Voy a utilizar rambling-trie en este ejemplo:

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 }

¿Y ahora estamos listos para probar la presencia de varias palabras en su matriz sin hacer un bucle, en tiempo O(log n) , con la misma simplicidad sintáctica que Array#include? , usando sublinear Trie#include? :

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


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


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