Ruby: ¿La Forma más Fácil de Filtrar las Claves Hash?
Tengo un hash que se ve algo como esto:
params = { :irrelevant => "A String",
:choice1 => "Oh look, another one",
:choice2 => "Even more strings",
:choice3 => "But wait",
:irrelevant2 => "The last string" }
Y quiero una forma sencilla de rechazar todas las claves que no son choice+int. Podría ser choice1, o choice1 a choice10. Varía.
¿Cómo singularizo las claves con solo la elección de palabra y un dígito o dígitos después de ellas?
Bono:
Convierte el hash en una cadena con tab (\t) como delimitador. Hice esto, pero tomó varias líneas de código. Por lo general, los maestros rubicianos pueden hacerlo en uno o dos alinear.
12 answers
NOTA: Si estás usando Rails, puedes usar Hash.slice
como indica lfx_cool en la respuesta a continuación.
Si estás usando Ruby, puedes usar el método select
. Necesitarás convertir la clave de un Símbolo a una Cadena para hacer la coincidencia de expresiones regulares. Esto te dará un nuevo Hash con solo las opciones en él.
choices = params.select { |key, value| key.to_s.match(/^choice\d+/) }
O puede usar delete_if
y modificar el Hash existente, por ejemplo,
params.delete_if { |key, value| !key.to_s.match(/choice\d+/) }
O si son solo las claves y no los valores que desea, entonces puede hacer:
params.keys.select { |key| key.to_s.match(/^choice\d+/) }
Y esto le dará al solo una matriz de las claves, por ejemplo, [:choice1, :choice2, :choice3]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-04 12:29:08
En Ruby, el Hash#select es una opción correcta. Si trabajas con Rails, puedes usar Hash # slice y Hash#slice!. por ejemplo (rails 3.2.13)
h1 = {:a => 1, :b => 2, :c => 3, :d => 4}
h1.slice(:a, :b) # return {:a=>1, :b=>2}, but h1 is not changed
h2 = h1.slice!(:a, :b) # h1 = {:a=>1, :b=>2}, h2 = {:c => 3, :d => 4}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-22 04:08:29
La forma más fácil es incluir el gem 'activesupport'
(o gem 'active_support'
).
Entonces, en tu clase solo necesitas
require 'active_support/core_ext/hash/slice'
Y llamar
params.slice(:choice1, :choice2, :choice3) # => {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}
Creo que no vale la pena declarar otras funciones que pueden tener errores, y es mejor usar un método que se ha ajustado durante los últimos años.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-16 10:11:25
La forma más fácil es incluir la gema 'activesupport' (o gema 'active_support').
params.slice(:choice1, :choice2, :choice3)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-27 07:27:48
Esta es una línea para resolver la pregunta original completa:
params.select { |k,_| k[/choice/]}.values.join('\t')
Pero la mayoría de las soluciones anteriores están resolviendo un caso en el que necesita conocer las claves con anticipación, usando slice
o regexp simple.
Aquí hay otro enfoque que funciona para casos de uso simples y más complejos, que se puede intercambiar en tiempo de ejecución
data = {}
matcher = ->(key,value) { COMPLEX LOGIC HERE }
data.select(&matcher)
Ahora no solo esto permite una lógica más compleja sobre la coincidencia de las claves o los valores, sino que también es más fácil de probar, y puede intercambiar la coincidencia lógica en tiempo de ejecución.
Ex para resolver el problema original:
def some_method(hash, matcher)
hash.select(&matcher).values.join('\t')
end
params = { :irrelevant => "A String",
:choice1 => "Oh look, another one",
:choice2 => "Even more strings",
:choice3 => "But wait",
:irrelevant2 => "The last string" }
some_method(params, ->(k,_) { k[/choice/]}) # => "Oh look, another one\\tEven more strings\\tBut wait"
some_method(params, ->(_,v) { v[/string/]}) # => "Even more strings\\tThe last string"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-07 22:22:04
Si trabajas con rails y tienes las claves en una lista separada, puedes usar la notación *
:
keys = [:foo, :bar]
hash1 = {foo: 1, bar:2, baz: 3}
hash2 = hash1.slice(*keys)
=> {foo: 1, bar:2}
Como se indica en otras respuestas, también puede usar slice!
para modificar el hash en su lugar (y devolver la clave/los valores borrados).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-30 16:21:05
Pon esto en un inicializador
class Hash
def filter(*args)
return nil if args.try(:empty?)
if args.size == 1
args[0] = args[0].to_s if args[0].is_a?(Symbol)
self.select {|key| key.to_s.match(args.first) }
else
self.select {|key| args.include?(key)}
end
end
end
Entonces puedes hacer
{a: "1", b: "b", c: "c", d: "d"}.filter(:a, :b) # => {a: "1", b: "b"}
O
{a: "1", b: "b", c: "c", d: "d"}.filter(/^a/) # => {a: "1"}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-10 14:32:50
Con Hash::select
:
params = params.select { |key, value| /^choice\d+$/.match(key.to_s) }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-15 11:57:31
Si quieres el hash restante:
params.delete_if {|k, v| ! k.match(/choice[0-9]+/)}
O si solo quieres las claves:
params.keys.delete_if {|k| ! k.match(/choice[0-9]+/)}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-15 11:59:55
params.select{ |k,v| k =~ /choice\d/ }.map{ |k,v| v}.join("\t")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-22 04:31:27
En cuanto a la pregunta de bonificación:
-
Si tiene salida de
#select
método como este (lista de matrices de 2 elementos):[[:choice1, "Oh look, another one"], [:choice2, "Even more strings"], [:choice3, "But wait"]]
Entonces simplemente toma este resultado y ejecuta:
filtered_params.join("\t") # or if you want only values instead of pairs key-value filtered_params.map(&:last).join("\t")
-
Si tienes salida de
#delete_if
método como este (hash):{:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}
Entonces:
filtered_params.to_a.join("\t") # or filtered_params.values.join("\t")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-15 12:12:53
params = { :irrelevant => "A String",
:choice1 => "Oh look, another one",
:choice2 => "Even more strings",
:choice3 => "But wait",
:irrelevant2 => "The last string" }
choices = params.select { |key, value| key.to_s[/^choice\d+/] }
#=> {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-17 14:39:46