2011-03-06 21 views
8

Creé este método para encontrar la palabra más larga en una matriz, pero me pregunto si hay una mejor manera de hacerlo. Soy bastante nuevo para Ruby, y lo hice como ejercicio para aprender el método inject.Ruby palabra más larga en matriz

Devuelve la palabra más larga en una matriz o una matriz de las palabras iguales más largas.

class Array 
    def longest_word 
    # Convert array elements to strings in the event that they're not. 
    test_array = self.collect { |e| e.to_s } 
    test_array.inject() do |word, comparison| 
     if word.kind_of?(Array) then 
     if word[0].length == comparison.length then 
      word << comparison 
     else 
      word[0].length > comparison.length ? word : comparison 
     end 
     else 
     # If words are equal, they are pushed into an array 
     if word.length == comparison.length then 
      the_words = Array.new 
      the_words << word 
      the_words << comparison 
     else 
      word.length > comparison.length ? word : comparison 
     end 
     end 
    end 
    end 
end 

Respuesta

27

lo haría

class Array 
    def longest_word 
    group_by(&:size).max.last 
    end 
end 
+1

si está seguro de que solo tiene cadenas, puede escribirlo como 'self.group_by (& length) .max.last'; de lo contrario, necesitaría una llamada' to_s' adicional como 'self.group_by {| el | el.to_s .size} .max.last'. Y finalmente, para devolver una sola cadena cuando solo tiene un elemento, puede hacer 'longest_word.size> 1? longest_word: longest_word.first' antes de regresar. –

+0

¡Agradable! Me tomé la libertad de hacer que el código sea más conciso, pero revertir si no te gusta el estilo –

+0

El OP dijo en la pregunta que lo hizo para aprender a 'inyectar', y no está del todo claro si estaba buscando para una mejor manera de usar 'inyectar' o simplemente un mejor período de manera. La suya es una respuesta bastante buena para la segunda interpretación y la de @Mladen Jablanović para la primera. Ambos obtienen mi +1. –

6

Ruby tiene un método estándar para devolver un elemento en una lista con el máximo de un valor.

anArray.max{|a, b| a.length <=> b.length} 

o puede utilizar el método max_by

anArray.max_by(&:length) 

para obtener todos los elementos con la longitud máxima

max_length = anArray.max_by(&:length).length 
all_with_max_length = anArray.find_all{|x| x.length = max_length} 
+0

Eso es bueno, pero el PO "acaba de hacer esto como un ejercicio para aprender el método de inyección" –

+3

Ambos métodos solo devuelven un elemento único, no una matriz de cadenas de igual longitud. –

1

A dos liner:

vc = ['asd','s','1234','1235'].sort{|a,b| b.size <=> a.size} 
vc.delete_if{|a| a.size < vc.first.size} 


#Output 
["1235", "1234"] 

o si quieres usar inyectar, esto usa tu idea, pero es m mineral corto

test_array.inject{ |ret,word| 
    ret = [ret] unless ret.kind_of?(Array) 

    ret << word if word.size == ret.first.size 
    ret = [word] if word.size > ret.first.size 
    ret 
} 
+0

Este método no es idempotente cuando se aplica directamente en una matriz. Probablemente no sea lo que esperarías. Podría usar un 'clon' aquí.Pero la solución está bien, y probablemente ni siquiera la más lenta (sin conocer los detalles de implementación del método incorporado 'group_by') –

+1

@Holger:' group_by' usa búsquedas de matriz, por lo que básicamente es 'O (n)', mientras que 'sort' es más lento. –

+0

En lugar de 'delete_if', puede usar' take_while' que se detendrá tan pronto como llegue a una palabra más pequeña. –

1
module Enumerable 
    def longest_word 
    (strings = map(&:to_s)). 
     zip(strings.map(&:length)). 
     inject([[''],0]) {|(wws, ll), (w, l)| 
     case l <=> ll 
     when -1 then [wws, ll] 
     when 1 then [[w], l] 
     else   [wws + [w], ll] 
     end 
     }.first 
    end 
end 

Este método sólo depende de Enumerable métodos genéricos, no hay nada Array específica al respecto, por lo tanto podemos tire hacia arriba en el módulo Enumerable, donde también estará disponible para Set s o Enumerator s, no solo Array s.

+0

Esto devuelve solo un elemento, no una matriz de palabras con tamaños iguales. – steenslag

+0

@steenslag: Sí, gracias por notarlo. El nombre del método me sorprendió un poco, hubiera esperado un método que devuelva varias palabras para llamarse 'longest_words' (plural). –

4

Esto es una usando inject (no funciona para una matriz vacía):

words.inject(['']){|a,w| 
    case w.length <=> a.last.length 
    when -1 
    a 
    when 0 
    a << w 
    when 1 
    [w] 
    end 
} 

que puede ser acortado a

words.inject(['']){|a,w| 
    [a + [w], [w], a][w.length <=> a.last.length] 
} 

para aquellos a quien le gusta el golf

+0

En realidad, esto * debería * funcionar para una matriz vacía, eso es (uno) de los puntos de proporcionar explícitamente un valor inicial para el acumulador. –

+0

En mi versión, de alguna manera pensé que sería mucho más limpio pasar la longitud de las palabras implícitamente a través del acumulador, pero no lo ha limpiado mucho :-) El tuyo se ve mucho mejor. –

+0

Para trabajar con matrices vacías, uno tendría que poner en marcha 'inject' con una matriz vacía (en lugar de una matriz que contiene una cadena vacía como yo), y complicar la ramificación dentro de un poco más. Pero para el propósito de aprender 'inyectar', pensé que esto era suficiente. –

0

Esta solución usa el método de inyección para acumular las cadenas más largas en una matriz, luego selecciona las que tienen la longitud más alta.

animales = [ "ratón", "cat", "ave", "tener", "alces"]

animals.inject (Hash.new {| h, k | h [k] = [ ]}) {| acc, e | acc [e.size] < < e; acc} .sort.last [1]

Esto devuelve: [ "ratón", "ratón"]

Cuestiones relacionadas