2010-06-15 31 views
40

¿Hay alguna forma de aplanar un hash en una cadena, con delimitadores opcionales entre claves y valores, y pares clave/valor?Aplanar el hash en un hilo en Ruby

Por ejemplo, print {:a => :b, :c => :d}.flatten('=','&') debe imprimir a=b&c=d

escribí algo de código para hacer esto, pero me preguntaba si había una manera más ordenada:

class Hash 
    def flatten(keyvaldelimiter, entrydelimiter) 
    string = "" 
    self.each do 
     |key, value| 
     key = "#{entrydelimiter}#{key}" if string != "" #nasty hack 
     string += "#{key}#{keyvaldelimiter}#{value}" 
    end 
    return string 
    end 
end 

print {:a => :b, :c => :d}.flatten('=','&') #=> 'c=d&a=b' 

Gracias

Respuesta

92

no me anular .flatten , que ya está definido:

devuelve una nueva matriz que es un aplanamiento unidimensional de este hash. Es decir, para cada clave o valor que sea una matriz, extraiga sus elementos en la nueva matriz. A diferencia de Array # flatten, este método no se aplana recursivamente de de manera predeterminada. Si el argumento de nivel opcional determina el nivel de recursión para aplanar.

Esta es la forma más sencilla de hacerlo que yo sepa:

{:a => 100, :b => 200}.map{|k,v| "#{k}=#{v}"}.join('&') 
# => "a=100&b=200"
+5

Puede simplificar aún más esto. Ver mi respuesta para más detalles. Lo esencial es reemplazar '| k, v |' con '| e |' que hace que 'e' sea una matriz a la que puedes llamar 'e.join (' = ')' activado. –

+1

cómo hacer lo inverso para obtener un hash – jenuine

4

Bueno, se podría hacerlo con métodos y matrices estándar:

class Hash 
    def flatten(keyvaldelimiter, entrydelimiter) 
    self.map { |k, v| "#{k}#{keyvaldelimiter}#{v}" }.join(entrydelimiter) 
    end 
end 

También te puede estar interesado en Rails to_query method.

También, obviamente, se puede escribir como "#{k}#{keyvaldelimiter}#{v}"k.to_s + keyvaldelimiter + v.to_s ...

+2

O '[k, v] .join (keyvaldelimiter)'. – henrikhodne

1

No estoy seguro si hay una manera integrada, pero aquí hay un código más corto:

class Hash 
    def flatten(kvdelim='', entrydelim='') 
    self.inject([]) { |a, b| a << b.join(kvdelim) }.join(entrydelim) 
    end 
end 

puts ({ :a => :b, :c => :d }).flatten('=', '&') # => a=b&c=d 
21

Una pequeña variación de la versión de @ elektronaut:

En realidad se puede poner sólo un |e| allí en vez de |k, v| en que caso e es una matriz de dos elementos y puede llamar al e.join('='). En total, tiene algo así como

class Hash 
    def join(keyvaldelim=$,, entrydelim=$,) # $, is the global default delimiter 
    map {|e| e.join(keyvaldelim) }.join(entrydelim) 
    end 
end 

{a: 100, b: 200}.join('=', '&') # I love showing off the new Ruby 1.9 Hash syntax 
# => 'a=100&b=200' 
+5

Me preguntaría seriamente qué tan inteligente es para el parche de mono 'Hash', especialmente con un nombre de método sin nombre como' join'. Si bien espero esto de muchos, estoy muy sorprendido de ver tal sugerencia de un desarrollador con mucha experiencia. – akostadinov

0

¿Qué pasa si lo recibimos en formato JSON. De esa forma el formato es claro.

Hay muchas herramientas JSON para ruby. Nunca los probé, sin embargo. Pero la salida se deslizará a javascript, etc., más fácilmente. Probablemente una línea.

El gran ahorro, sin embargo, estaría en la documentación, en el sentido de que necesitará mucho menos.

4

Si está intentando generar una cadena de consulta url, sin duda debe utilizar un método como activesupport's to_param (aliased to_query). Imagine los problemas si tiene un signo de & signo igual en los datos en sí.

to_query se encarga de escape:

>> require 'active_support/core_ext/object' 
>> {'a&' => 'b', 'c' => 'd'}.to_query 
>> => "a%26=b&c=d" 

EDITAR

@fahadsadah hace un buen punto de no querer cargar rieles. Incluso active_support/core_ext/object cargará 71 archivos. También monos parches de las clases principales.

Una solución ligera y más limpio:

require 'rack' # only loads 3 files 
{'a&' => 'b', 'c' => 'd'}.map{|pair| 
    pair.map{|e| Rack::Utils.escape(e.to_s) }.join('=') 
}.join('&') 
# => "a%26=b&c=d" 

Es importante escapar, de lo contrario la operación no es reversible.

+0

Eso está muy bien, y es un buen punto, pero prefiero no requerir que se instale Rails cuando creé el hash de origen –

+0

Rails for the win !! –