index create array ruby

create - ruby select



En Ruby, ¿hay un método Array que combine ''seleccionar'' y ''mapa''? (13)

Aquí hay un ejemplo. No es lo mismo que su problema, pero puede ser lo que quiera o puede dar una pista sobre su solución:

def example lines.each do |x| new_value = do_transform(x) if new_value == some_thing return new_value # here jump out example method directly. else next # continue next iterate. end end end

Tengo una matriz de Ruby que contiene algunos valores de cadena. Necesito:

  1. Encuentre todos los elementos que coincidan con algún predicado
  2. Ejecutar los elementos coincidentes a través de una transformación
  3. Devuelve los resultados como una matriz

En este momento mi solución se ve así:

def example matchingLines = @lines.select{ |line| ... } results = matchingLines.map{ |line| ... } return results.uniq.sort end

¿Existe un método Array o Enumerable que combine select y map en una sola declaración lógica?


Creo que de esta manera es más legible, porque divide las condiciones del filtro y el valor mapeado, mientras que queda claro que las acciones están conectadas:

results = @lines.select { |line| line.should_include? }.map do |line| line.value_to_map end

Y, en su caso específico, elimine la variable de result todos juntos:

def example @lines.select { |line| line.should_include? }.map { |line| line.value_to_map }.uniq.sort end


Debería intentar usar mi biblioteca Rearmed Ruby en la que he agregado el método Enumerable#select_map . Aquí hay un ejemplo:

items = [{version: "1.1"}, {version: nil}, {version: false}] items.select_map{|x| x[:version]} #=> [{version: "1.1"}] # or without enumerable monkey patch Rearmed.select_map(items){|x| x[:version]}


No estoy seguro de que haya uno. El módulo Enumerable , que agrega select y map , no muestra uno.

select_and_transform pasar en dos bloques el método select_and_transform , que sería un poco intuitivo en mi humilde opinión.

Obviamente, podrías encadenarlos, lo cual es más legible:

transformed_list = lines.select{|line| ...}.map{|line| ... }


No, pero puedes hacerlo así:

lines.map { |line| do_some_action if check_some_property }.reject(&:nil?)

O mejor:

lines.inject([]) { |all, line| all << line if check_some_property; all }


Otra forma diferente de abordar esto es usar lo nuevo (en relación con esta pregunta) Enumerator::Lazy :

def example @lines.lazy .select { |line| line.property == requirement } .map { |line| transforming_method(line) } .uniq .sort end

El método .lazy devuelve un enumerador perezoso. Llamar .select o .map en un enumerador vago devuelve otro enumerador perezoso. Solo cuando llama a .uniq hace forzar al enumerador y devolver una matriz. Entonces, ¿qué ocurre de manera efectiva? .select llamadas .select y .map se combinan en una: solo itera sobre @lines una vez para hacer ambas .select y .map .

Mi instinto es que el método de reduce de Adam será un poco más rápido, pero creo que es mucho más legible.

La consecuencia principal de esto es que no se crean objetos de matriz intermedios para cada llamada de método posterior. En una situación @lines.select.map normal, select devuelve una matriz que luego se modifica mediante un map , devolviendo de nuevo una matriz. En comparación, la evaluación perezosa solo crea una matriz una vez. Esto es útil cuando su objeto de colección inicial es grande. También te permite trabajar con enumeradores infinitos, por ejemplo random_number_generator.lazy.select(&:odd?).take(10) .


Puede usar reduce para esto, que requiere solo una pasada:

[1,1,1,2,3,4].reduce([]) { |a, n| a.push(n*3) if n==1; a } => [3, 3, 3]

En otras palabras, inicialice el estado para que sea lo que desea (en nuestro caso, una lista vacía para completar: [] ), luego siempre asegúrese de devolver este valor con modificaciones para cada elemento en la lista original (en nuestro caso, el elemento modificado empujado a la lista).

Este es el más eficiente ya que solo hace un bucle sobre la lista con una pasada ( map + select o compact requiere dos pases).

En tu caso:

def example results = @lines.reduce([]) do |lines, line| lines.push( ...(line) ) if ... lines end return results.uniq.sort end


Respuesta simple:

Si tiene n registros, y desea select y map según la condición, entonces

records.map { |record| record.attribute if condition }.compact

Aquí, atributo es lo que quieras del registro y la condición en que puedes poner cualquier control.

compacto es eliminar el nil innecesario que salió de esa condición si


Si no desea crear dos matrices diferentes, ¡puede usar compact! pero ten cuidado al respecto.

array = [1,1,1,2,3,4] new_array = map{|n| n*3 if n==1} new_array.compact!

¡Curiosamente, compact! hace una eliminación en lugar de nil. El valor de retorno de compact! es la misma matriz si hubo cambios pero nulos si no hubo nulos.

array = [1,1,1,2,3,4] new_array = map{|n| n*3 if n==1}.tap { |array| array.compact! }

Sería un trazador de líneas.


Si tiene una select que puede usar el operador de case ( === ), grep es una buena alternativa:

p [1,2,''not_a_number'',3].grep(Integer){|x| -x } #=> [-1, -2, -3] p [''1'',''2'',''not_a_number'',''3''].grep(//D/, &:upcase) #=> ["NOT_A_NUMBER"]

Si necesitamos una lógica más compleja, podemos crear lambdas:

my_favourite_numbers = [1,4,6] is_a_favourite_number = -> x { my_favourite_numbers.include? x } make_awesome = -> x { "***#{x}***" } my_data = [1,2,3,4] p my_data.grep(is_a_favourite_number, &make_awesome) #=> ["***1***", "***4***"]


Tu versión:

def example matchingLines = @lines.select{ |line| ... } results = matchingLines.map{ |line| ... } return results.uniq.sort end

Mi version:

def example results = {} @lines.each{ |line| results[line] = true if ... } return results.keys.sort end

Esto hará 1 iteración (excepto el tipo), y tiene la ventaja añadida de mantener la singularidad (si no te importa uniq, simplemente haz resultados como una matriz y results.push(line) if ...


Usualmente uso map y compact juntos junto con mis criterios de selección como postfix if . compact se deshace de los nils.

jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1} => [3, 3, 3, nil, nil, nil] jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}.compact => [3, 3, 3]


def example @lines.select {|line| ... }.map {|line| ... }.uniq.sort end

En Ruby 1.9 y 1.8.7, también puede encadenar y ajustar iteradores simplemente sin pasarles un bloque:

enum.select.map {|bla| ... }

Pero no es posible en este caso, ya que los tipos de valores de retorno de bloque de select y map no coinciden. Tiene más sentido para algo como esto:

enum.inject.with_index {|(acc, el), idx| ... }

AFAICS, lo mejor que puedes hacer es el primer ejemplo.

Aquí hay un pequeño ejemplo:

%w[a b 1 2 c d].map.select {|e| if /[0-9]/ =~ e then false else e.upcase end } # => ["a", "b", "c", "d"] %w[a b 1 2 c d].select.map {|e| if /[0-9]/ =~ e then false else e.upcase end } # => ["A", "B", false, false, "C", "D"]

Pero lo que realmente quieres es ["A", "B", "C", "D"] .