2009-07-10 4 views
20

Spero che questa domanda abbia una risposta molto semplice. Riesco a pensare a modi con cui fare cicli noiosi e fastidiosi, ma spero che ci sia una soluzione più elegante.Ruby: dividere un hash in due in base ai tasti desiderati

se ho le seguenti due variabili:

hash = {:a => 1, :b => 2, :c => 3, :d => 4} 
keyset = [:a, :c] 

Come posso ottenere i seguenti due hash nel modo più semplice possibile?

hash1 = {:a => 1, :c => 3} 
hash2 = {:b => 3, :d => 4} 

Se l'esempio non fa il mio obiettivo chiaro, in sostanza, quello che voglio è un ibrido tra #delete e #delete_if - #delete restituisce il valore eliminato, mentre #delete_if mi permette di cancellare in massa. Preferirei un modo per eliminare alla rinfusa e avere i valori cancellati restituiti - o qualcosa di equivalente.

Grazie!

risposta

31

Prova supporto attivo con Hash#slice e/o Hash#except. Esistono anche metodi Bang:

$ irb 
>> require 'active_support/core_ext' 
=> true 

>> hash = {:a => 1, :b => 2, :c => 3, :d => 4} 
=> {:a=>1, :d=>4, :b=>2, :c=>3} 
>> keyset = [:a, :c] 
=> [:a, :c] 

>> remainders = hash.slice!(*keyset) 
=> {:d=>4, :b=>2} 

>> remainders 
=> {:d=>4, :b=>2} 
>> hash 
=> {:a=>1, :c=>3} 
+0

meraviglioso - grazie! – Matchu

12
new_hash = {} 
keyset.each {|i| new_hash[i] = hash.delete(i)} 

che sembrava di farlo per me, senza tirare in ulteriori requisiti

+0

Non ho detto che ero su Rails - dal momento che sembrava irrilevante - ma ciò significa che ho già un supporto attivo. Ma grazie, ancora! :) – Matchu

11
hash = { a: 1, b: 2, c: 3, d: 4 } 
keyset = [:a, :c] 

left, right = hash.partition {|k,v| keyset.include? k } 

questo lascia a destra ea sinistra, come array di array; tornare indietro nel hash:

left = Hash[left] 
right = Hash[right] 

puts "left=#{left.inspect}" 
puts "right=#{right.inspect}" 
+4

Sembra un difetto di design di Ruby che '.partition' non è la risposta da solo; dato che hash # hash e 'hash # reject' restituisce hash, avrebbe senso' Hash # partition' per restituire un array di due hash. Ma forse mi manca qualcosa. –

+3

Se vuoi recuperare un hash (in Ruby 2.0+) puoi fare 'left, right = hash.partition {| k, v | keyset.include? (k)} .map (&: to_h) ' – pnomolos

0

Se non ti dispiace dipendenze esterne che è possibile utilizzare https://github.com/renra/split-off-ruby Poi si può fare:

hash2 = hash.split_off!(:b, :d) 

hash conterrà ancora i valori originali per le chiavi: una e: c . I metodi di cui sopra sono abbastanza buoni, ma a volte è meglio essere espressivi riguardo al tuo intento con il giusto nome del metodo, credo.

1

con Rails/Supporto Active è possibile utilizzare il metodo extract!:

hash = {:a => 1, :b => 2, :c => 3, :d => 4} 
keyset = [:a, :c] 

hash2 = hash.extract! *keyset 
>> {:a=>1, :c=>3} 

hash 
>> {:b=>2, :d=>4}