into - Ruby: ¿La forma más fácil de filtrar las teclas hash?
ruby puts hash (12)
Con Hash::select
:
params = params.select { |key, value| /^choice/d+$/.match(key.to_s) }
Tengo un hash que se ve así:
params = { :irrelevant => "A String",
:choice1 => "Oh look, another one",
:choice2 => "Even more strings",
:choice3 => "But wait",
:irrelevant2 => "The last string" }
Y quiero una forma simple de rechazar todas las claves que no son choice + int. Podría ser choice1, o choice1 a choice10. Varía.
¿Cómo selecciono las teclas con solo la palabra seleccionada y un dígito o dígitos después de ellas?
Prima:
Convierta el hash en una cadena con tabulador (/ t) como delimitador. Hice esto, pero tomó varias líneas de código. Por lo general, los maestros de Rubio pueden hacerlo en una o más líneas.
En Ruby, la selección de Hash # es una opción correcta. Si trabajas con Rails, puedes usar Hash # slice y Hash # slice !. ej. (rieles 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}
En cuanto a la pregunta de bonificación:
Si tiene salida del método
#select
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 tiene salida del método
#delete_if
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")
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 resuelven un caso en el que necesitas saber las claves antes de tiempo, usando slice
o simple regexp.
Aquí hay otro enfoque que funciona para casos de uso simples y más complejos, que es intercambiable 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 para hacer coincidir las claves o los valores, sino que también es más fácil de probar, y puede intercambiar la lógica correspondiente en el 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"
La forma más fácil es incluir la gem ''activesupport''
(o gem ''active_support''
).
Entonces, en tu clase solo necesitas
require ''active_support/core_ext/hash/slice''
y para 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 puedan tener errores, y es mejor usar un método que se haya modificado durante los últimos años.
La forma más fácil es incluir la gema ''activesupport'' (o gema ''active_support'').
params.slice(:choice1, :choice2, :choice3)
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"}
Si quieres el hash restante:
params.delete_if {|k, v| ! k.match(/choice[0-9]+/)}
o si solo quieres las llaves:
params.keys.delete_if {|k| ! k.match(/choice[0-9]+/)}
Si trabaja con rieles y tiene las claves en una lista separada, puede usar la notación *
:
keys = [:foo, :bar]
hash1 = {foo: 1, bar:2, baz: 3}
hash2 = hash1.slice(*keys)
=> {foo: 1, bar:2}
Como otras respuestas indicadas, ¡también puedes usar slice!
para modificar el hash en su lugar (y devolver la clave / valores borrados).
NOTA: Si está utilizando Rails, puede usar Hash.slice
como lo indica lfx_cool en la respuesta a continuación.
Si está usando Ruby, puede usar el método de select
. Tendrá que convertir la clave de un Símbolo a una Cadena para hacer la coincidencia de expresión regular. Esto le 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 ej.
params.delete_if { |key, value| !key.to_s.match(/choice/d+/) }
o si solo son 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á a la justa una Matriz de las teclas, por ejemplo [:choice1, :choice2, :choice3]
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"}
params.select{ |k,v| k =~ /choice/d/ }.map{ |k,v| v}.join("/t")