ruby-on-rails - example - hash en ruby on rails
¿Cómo eliminar una clave de Hash y obtener el hash restante en Ruby/Rails? (13)
Para agregar un nuevo par a Hash hago:
{:a => 1, :b => 2}.merge!({:c => 3}) #=> {:a => 1, :b => 2, :c => 3}
¿Hay una manera similar de eliminar una clave de Hash?
Esto funciona:
{:a => 1, :b => 2}.reject! { |k| k == :a } #=> {:b => 2}
pero esperaría tener algo como:
{:a => 1, :b => 2}.delete!(:a) #=> {:b => 2}
Es importante que el valor de retorno sea el hash restante, por lo que podría hacer cosas como:
foo(my_hash.reject! { |k| k == my_key })
en una linea
¿Por qué no solo usar:
hash.delete(key)
En lugar de parchear monos o incluir bibliotecas grandes innecesariamente, puede usar refinamientos si está usando Ruby 2 :
module HashExtensions
refine Hash do
def except!(*candidates)
candidates.each { |candidate| delete(candidate) }
self
end
def except(*candidates)
dup.remove!(candidates)
end
end
end
Puede usar esta función sin afectar otras partes de su programa, o tener que incluir grandes bibliotecas externas.
class FabulousCode
using HashExtensions
def incredible_stuff
delightful_hash.except(:not_fabulous_key)
end
end
Esta es una forma de una línea para hacerlo, pero no es muy legible. Recomiendo usar dos líneas en su lugar.
use_remaining_hash_for_something(Proc.new { hash.delete(:key); hash }.call)
Esto también funcionaría: hash[hey] = nil
Fue genial si eliminar devolver el par de eliminación del hash. Estoy haciendo esto:
hash = {a: 1, b: 2, c: 3}
{b: hash.delete(:b)} # => {:b=>2}
hash # => {:a=>1, :c=>3}
Hay muchas formas de eliminar una clave de un hash y obtener el hash restante en Ruby.
.slice
=>.slice
claves seleccionadas y no las eliminará del hash original2.2.2 :074 > hash = {"one"=>1, "two"=>2, "three"=>3} => {"one"=>1, "two"=>2, "three"=>3} 2.2.2 :075 > hash.slice("one","two") => {"one"=>1, "two"=>2} 2.2.2 :076 > hash => {"one"=>1, "two"=>2, "three"=>3}
.delete
=> Eliminará las claves seleccionadas del hash original (puede aceptar solo una clave y no más de una)2.2.2 :094 > hash = {"one"=>1, "two"=>2, "three"=>3} => {"one"=>1, "two"=>2, "three"=>3} 2.2.2 :095 > hash.delete("one") => 1 2.2.2 :096 > hash => {"two"=>2, "three"=>3}
.except
=>.except
las claves restantes pero no eliminará nada del hash original2.2.2 :097 > hash = {"one"=>1, "two"=>2, "three"=>3} => {"one"=>1, "two"=>2, "three"=>3} 2.2.2 :098 > hash.except("one","two") => {"three"=>3} 2.2.2 :099 > hash => {"one"=>1, "two"=>2, "three"=>3}
.delete_if
=> En caso de que necesite eliminar una clave basada en un valor. Obviamente, eliminará las claves correspondientes del hash original2.2.2 :115 > hash = {"one"=>1, "two"=>2, "three"=>3, "one_again"=>1} => {"one"=>1, "two"=>2, "three"=>3, "one_again"=>1} 2.2.2 :116 > value = 1 => 1 2.2.2 :117 > hash.delete_if { |k,v| v == value } => {"two"=>2, "three"=>3} 2.2.2 :118 > hash => {"two"=>2, "three"=>3}
Resultados basados en Ruby 2.2.2.
Oneliner plain ruby, solo funciona con ruby> 1.9.x:
1.9.3p0 :002 > h = {:a => 1, :b => 2}
=> {:a=>1, :b=>2}
1.9.3p0 :003 > h.tap { |hs| hs.delete(:a) }
=> {:b=>2}
Tap método Tap siempre devuelve el objeto sobre el que se invoca ...
De lo contrario, si ha requerido active_support/core_ext/hash
(que se requiere automáticamente en cada aplicación de Rails), puede usar uno de los siguientes métodos según sus necesidades:
➜ ~ irb
1.9.3p125 :001 > require ''active_support/core_ext/hash'' => true
1.9.3p125 :002 > h = {:a => 1, :b => 2, :c => 3}
=> {:a=>1, :b=>2, :c=>3}
1.9.3p125 :003 > h.except(:a)
=> {:b=>2, :c=>3}
1.9.3p125 :004 > h.slice(:a)
=> {:a=>1}
except utiliza un enfoque de lista negra, por lo que elimina todas las claves enumeradas como argumentos, mientras que slice usa un enfoque de lista blanca, por lo que elimina todas las claves que no figuran como argumentos. También existe la versión de bang de aquellos métodos ( except!
Y slice!
) Que modifican el hash dado pero su valor de retorno es diferente, ambos devuelven un hash. Representa las claves eliminadas para slice!
y las llaves que se guardan para la except!
:
1.9.3p125 :011 > {:a => 1, :b => 2, :c => 3}.except!(:a)
=> {:b=>2, :c=>3}
1.9.3p125 :012 > {:a => 1, :b => 2, :c => 3}.slice!(:a)
=> {:b=>2, :c=>3}
Puedes usar except!
de la facets
joya:
>> require ''facets'' # or require ''facets/hash/except''
=> true
>> {:a => 1, :b => 2}.except(:a)
=> {:b=>2}
El hash original no cambia.
EDITAR: como dice Russel, facetas tiene algunos problemas ocultos y no es completamente compatible con API con ActiveSupport. Por otro lado, ActiveSupport no es tan completo como las facetas. Al final, usaría AS y dejaría los casos de borde en su código.
Si desea usar Ruby puro (sin Rails), no quiera crear métodos de extensión (quizás necesite esto solo en uno o dos lugares y no quiera contaminar el espacio de nombres con toneladas de métodos) y no quiera Editar hash en su lugar (es decir, eres fanático de la programación funcional como yo), puedes ''seleccionar'':
>> x = {:a => 1, :b => 2, :c => 3}
=> {:a=>1, :b=>2, :c=>3}
>> x.select{|x| x != :a}
=> {:b=>2, :c=>3}
>> x.select{|x| ![:a, :b].include?(x)}
=> {:c=>3}
>> x
=> {:a=>1, :b=>2, :c=>3}
Ver Ruby on Rails: eliminar múltiples claves hash
hash.delete_if{ |k,| keys_to_delete.include? k }
en rubí puro:
{:a => 1, :b => 2}.tap{|x| x.delete(:a)} # => {:b=>2}
Rails tiene un except / except! Método que devuelve el hash con esas claves eliminadas. Si ya está usando Rails, no tiene sentido crear su propia versión de esto.
class Hash
# Returns a hash that includes everything but the given keys.
# hash = { a: true, b: false, c: nil}
# hash.except(:c) # => { a: true, b: false}
# hash # => { a: true, b: false, c: nil}
#
# This is useful for limiting a set of parameters to everything but a few known toggles:
# @person.update(params[:person].except(:admin))
def except(*keys)
dup.except!(*keys)
end
# Replaces the hash without the given keys.
# hash = { a: true, b: false, c: nil}
# hash.except!(:c) # => { a: true, b: false}
# hash # => { a: true, b: false }
def except!(*keys)
keys.each { |key| delete(key) }
self
end
end
#in lib/core_extensions.rb
class Hash
#pass single or array of keys, which will be removed, returning the remaining hash
def remove!(*keys)
keys.each{|key| self.delete(key) }
self
end
#non-destructive version
def remove(*keys)
self.dup.remove!(*keys)
end
end
#in config/initializers/app_environment.rb (or anywhere in config/initializers)
require ''core_extensions''
He configurado esto para que .remove devuelva una copia del hash con las claves eliminadas, ¡mientras elimina! modifica el hash mismo. Esto está en consonancia con las convenciones de rubí. por ejemplo, desde la consola
>> hash = {:a => 1, :b => 2}
=> {:b=>2, :a=>1}
>> hash.remove(:a)
=> {:b=>2}
>> hash
=> {:b=>2, :a=>1}
>> hash.remove!(:a)
=> {:b=>2}
>> hash
=> {:b=>2}
>> hash.remove!(:a, :b)
=> {}