@JanDvorak Esta pregunta no solo se trata de devolver subhash sino también de modificar uno existente. Cosas muy similares, pero ActiveSupport tiene diferentes medios para lidiar con ellas.
skalee
Respuestas:
58
Si desea específicamente que el método devuelva los elementos extraídos pero h1 permanezca igual:
Esto es O (n2): tendrá un bucle en la selección, otro bucle en la inclusión que se llamará h1.size times.
metakungfu
1
Si bien esta respuesta es decente para rubí puro, si está usando rieles, la respuesta a continuación (usando una función integrada sliceo except, según sus necesidades) es mucho más limpia
Krease
137
ActiveSupport, Al menos desde 2.3.8, proporciona cuatro métodos convenientes: #slice, #excepty sus homólogos destructivos: #slice!y #except!. Se mencionaron en otras respuestas, pero para resumirlas en un solo lugar:
Tenga en cuenta los valores de retorno de los métodos bang. No solo adaptarán el hash existente, sino que también devolverán las entradas eliminadas (no guardadas). Se Hash#except!adapta mejor al ejemplo que se da en la pregunta:
x ={a:1, b:2, c:3, d:4}# => {:a=>1, :b=>2, :c=>3, :d=>4}
x.except!(:c,:d)# => {:a=>1, :b=>2}
x
# => {:a=>1, :b=>2}
ActiveSupportno requiere rieles completos, es bastante ligero. De hecho, muchas gemas que no son rieles dependen de él, por lo que probablemente ya lo tenga en Gemfile.lock. No es necesario extender la clase Hash por su cuenta.
El parche de Mokey es definitivamente el camino a seguir, en mi opinión. Mucho más limpio y aclara la intención.
Romário
1
Agregar para modificar el código para abordar correctamente el módulo central, definir el módulo e importar extender Hash core ... module CoreExtensions module Hash def slice (* keys) :: Hash [[keys, self.values_at (* keys)]. Transpose] end end end Hash.include CoreExtensions :: Hash
¡Creo que estás describiendo extracto !. ¡extraer! elimina las claves del hash inicial, devolviendo un nuevo hash que contiene las claves eliminadas. ¡rebanada! hace lo contrario: elimina todas las claves excepto las especificadas del hash inicial (nuevamente, devuelve un nuevo hash que contiene las claves eliminadas). ¡Así que corta! es un poco más como una operación de "retención".
Buen trabajo. No es exactamente lo que pide. Su método devuelve: {: d =>: D,: b =>: B,: e => nil,: f => nil} {: c =>: C,: a =>: A,: d => : D,: b =>: B}
Andy
Una solución equivalente de una línea (y quizás más rápida): <pre> def subhash(*keys) select {|k,v| keys.include?(k)} end
Aquí hay una solución funcional que puede ser útil si no está ejecutando Ruby 2.5 y en el caso de que no desee contaminar su clase Hash agregando un nuevo método:
Respuestas:
Si desea específicamente que el método devuelva los elementos extraídos pero h1 permanezca igual:
Y si quieres parchear eso en la clase Hash:
Si solo desea eliminar los elementos especificados del hash, es mucho más fácil usar delete_if .
fuente
slice
oexcept
, según sus necesidades) es mucho más limpiaActiveSupport
, Al menos desde 2.3.8, proporciona cuatro métodos convenientes:#slice
,#except
y sus homólogos destructivos:#slice!
y#except!
. Se mencionaron en otras respuestas, pero para resumirlas en un solo lugar:Tenga en cuenta los valores de retorno de los métodos bang. No solo adaptarán el hash existente, sino que también devolverán las entradas eliminadas (no guardadas). Se
Hash#except!
adapta mejor al ejemplo que se da en la pregunta:ActiveSupport
no requiere rieles completos, es bastante ligero. De hecho, muchas gemas que no son rieles dependen de él, por lo que probablemente ya lo tenga en Gemfile.lock. No es necesario extender la clase Hash por su cuenta.fuente
x.except!(:c, :d)
(con bang) debería ser# => {:a=>1, :b=>2}
. Bien si puede editar su respuesta.Si usa rieles , Hash # slice es el camino a seguir.
Si no usa rieles , Hash # values_at devolverá los valores en el mismo orden en que los solicitó para que pueda hacer esto:
ex:
Explicación:
Fuera de lo
{:a => 1, :b => 2, :c => 3}
que queremos{:a => 1, :b => 2}
Si siente que el parche de mono es el camino a seguir, lo siguiente es lo que desea:
fuente
Ruby 2.5 agregó rebanada de Hash # :
fuente
Puede usar slice! (* Keys) que está disponible en las extensiones principales de ActiveSupport
initial_hash ahora sería
diapositiva_extraída ahora sería
Puedes mirar
slice.rb in ActiveSupport 3.1.3
fuente
fuente
def subhash(*keys) select {|k,v| keys.include?(k)} end
fuente
si usa rieles, puede ser conveniente usar Hash, excepto
fuente
fuente
Aquí hay una comparación de rendimiento rápida de los métodos sugeridos,
#select
parece ser el más rápidoEl refinamiento se verá así:
Y para usarlo:
fuente
Ambos
delete_if
ykeep_if
son parte del núcleo de Ruby. Aquí puede lograr lo que le gustaría sin parchear elHash
tipo.Para obtener más información, consulte los enlaces a continuación de la documentación:
fuente
Como han mencionado otros, Ruby 2.5 agregó el método Hash # slice.
Rails 5.2.0beta1 también agregó su propia versión de Hash # slice para ajustar la funcionalidad a los usuarios del framework que están usando una versión anterior de Ruby. https://github.com/rails/rails/commit/01ae39660243bc5f0a986e20f9c9bff312b1b5f8
Si busca implementar el suyo por alguna razón, también es un buen trazador de líneas:
fuente
Este código inyecta la funcionalidad que está solicitando en la clase Hash:
y produce los resultados que proporcionó:
Nota: este método en realidad devuelve las claves / valores extraídos.
fuente
Aquí hay una solución funcional que puede ser útil si no está ejecutando Ruby 2.5 y en el caso de que no desee contaminar su clase Hash agregando un nuevo método:
Entonces puedes aplicarlo incluso en hashes anidados:
fuente
Solo una adición al método de corte, si las claves de subhash que desea separar del hash original van a ser dinámicas, puede hacer lo siguiente,
fuente
Podemos hacerlo haciendo un bucle en las claves que solo queremos extraer y simplemente verificando que la clave exista y luego extraerla.
fuente