Bir hashim var:
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
Bunun gibi bir alt karma çıkartmanın en iyi yolu nedir?
h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D}
h1 #=> {:a => :A, :c => :C}
Bir hashim var:
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
Bunun gibi bir alt karma çıkartmanın en iyi yolu nedir?
h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D}
h1 #=> {:a => :A, :c => :C}
Yanıtlar:
Yöntemin özellikle çıkarılan öğeleri döndürmesini, ancak h1'in aynı kalmasını istiyorsanız:
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h2 = h1.select {|key, value| [:b, :d, :e, :f].include?(key) } # => {:b=>:B, :d=>:D}
h1 = Hash[h1.to_a - h2.to_a] # => {:a=>:A, :c=>:C}
Ve bunu Hash sınıfına eklemek istiyorsanız:
class Hash
def extract_subhash(*extract)
h2 = self.select{|key, value| extract.include?(key) }
self.delete_if {|key, value| extract.include?(key) }
h2
end
end
Sadece belirtilen öğeleri hash'den kaldırmak istiyorsanız, delete_if kullanmak çok daha kolaydır .
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h1.delete_if {|key, value| [:b, :d, :e, :f].include?(key) } # => {:a=>:A, :c=>:C}
h1 # => {:a=>:A, :c=>:C}
slice
veya except
ihtiyaçlarınıza bağlı olarak) çok daha temiz
ActiveSupport
, 2.3.8 beri en az dört kullanışlı bir yöntem sağlar: #slice
, #except
ve onların yıkıcı karşılıkları: #slice!
ve #except!
. Başka cevaplarda da bahsedildi, ancak bunları tek bir yerde özetlemek gerekirse:
x = {a: 1, b: 2, c: 3, d: 4}
# => {:a=>1, :b=>2, :c=>3, :d=>4}
x.slice(:a, :b)
# => {:a=>1, :b=>2}
x
# => {:a=>1, :b=>2, :c=>3, :d=>4}
x.except(:a, :b)
# => {:c=>3, :d=>4}
x
# => {:a=>1, :b=>2, :c=>3, :d=>4}
Bang yöntemlerinin dönüş değerlerini not edin. Yalnızca mevcut hash'i uyarlamakla kalmaz, aynı zamanda kaldırılan (tutulmayan) girişleri de iade eder. Hash#except!
Takım elbise iyi söz verilen örnek:
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}
ActiveSupport
tam Rails gerektirmez, oldukça hafiftir. Aslında, pek çok ray dışı mücevher buna bağlıdır, bu yüzden büyük olasılıkla zaten Gemfile.lock'ta zaten var. Hash sınıfını kendi başınıza genişletmenize gerek yok.
x.except!(:c, :d)
(Patlama ile) sonucunun olması gerekir # => {:a=>1, :b=>2}
. Cevabınızı düzenleyebiliyorsanız iyi.
Ray kullanıyorsanız , Hash # slice gitmenin yoludur.
{:a => :A, :b => :B, :c => :C, :d => :D}.slice(:a, :c)
# => {:a => :A, :c => :C}
Eğer raylar kullanmazsanız , bunları sorduk olarak Hash # values_at aynı sırada değerlerini dönecektir Bunu böylece:
def slice(hash, *keys)
Hash[ [keys, hash.values_at(*keys)].transpose]
end
def except(hash, *keys)
desired_keys = hash.keys - keys
Hash[ [desired_keys, hash.values_at(*desired_keys)].transpose]
end
ör:
slice({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2)
# => {'bar' => 'foo', 2 => 'two'}
except({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2)
# => {:foo => 'bar'}
Açıklama:
Dışında {:a => 1, :b => 2, :c => 3}
istiyoruz{:a => 1, :b => 2}
hash = {:a => 1, :b => 2, :c => 3}
keys = [:a, :b]
values = hash.values_at(*keys) #=> [1, 2]
transposed_matrix =[keys, values].transpose #=> [[:a, 1], [:b, 2]]
Hash[transposed_matrix] #=> {:a => 1, :b => 2}
Maymun yamasının gitmenin yolu olduğunu düşünüyorsanız, istediğiniz şey şudur:
module MyExtension
module Hash
def slice(*keys)
::Hash[[keys, self.values_at(*keys)].transpose]
end
def except(*keys)
desired_keys = self.keys - keys
::Hash[[desired_keys, self.values_at(*desired_keys)].transpose]
end
end
end
Hash.include MyExtension::Hash
Ruby 2.5 , Hash # dilim ekledi :
h = { a: 100, b: 200, c: 300 }
h.slice(:a) #=> {:a=>100}
h.slice(:b, :c, :d) #=> {:b=>200, :c=>300}
ActiveSupport'un çekirdek uzantılarında bulunan dilim! (* Anahtarlarını) kullanabilirsiniz.
initial_hash = {:a => 1, :b => 2, :c => 3, :d => 4}
extracted_slice = initial_hash.slice!(:a, :c)
initial_hash artık
{:b => 2, :d =>4}
extracted_slide artık
{:a => 1, :c =>3}
Bakabilirsin slice.rb in ActiveSupport 3.1.3
module HashExtensions
def subhash(*keys)
keys = keys.select { |k| key?(k) }
Hash[keys.zip(values_at(*keys))]
end
end
Hash.send(:include, HashExtensions)
{:a => :A, :b => :B, :c => :C, :d => :D}.subhash(:a) # => {:a => :A}
def subhash(*keys) select {|k,v| keys.include?(k)} end
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
keys = [:b, :d, :e, :f]
h2 = (h1.keys & keys).each_with_object({}) { |k,h| h.update(k=>h1.delete(k)) }
#=> {:b => :B, :d => :D}
h1
#=> {:a => :A, :c => :C}
class Hash
def extract(*keys)
key_index = Hash[keys.map{ |k| [k, true] }] # depends on the size of keys
partition{ |k, v| key_index.has_key?(k) }.map{ |group| Hash[group] }
end
end
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h2, h1 = h1.extract(:b, :d, :e, :f)
İşte önerilen yöntemlerin hızlı bir performans karşılaştırması #select
, en hızlı gibi görünüyor
k = 1_000_000
Benchmark.bmbm do |x|
x.report('select') { k.times { {a: 1, b: 2, c: 3}.select { |k, _v| [:a, :b].include?(k) } } }
x.report('hash transpose') { k.times { Hash[ [[:a, :b], {a: 1, b: 2, c: 3}.fetch_values(:a, :b)].transpose ] } }
x.report('slice') { k.times { {a: 1, b: 2, c: 3}.slice(:a, :b) } }
end
Rehearsal --------------------------------------------------
select 1.640000 0.010000 1.650000 ( 1.651426)
hash transpose 1.720000 0.010000 1.730000 ( 1.729950)
slice 1.740000 0.010000 1.750000 ( 1.748204)
----------------------------------------- total: 5.130000sec
user system total real
select 1.670000 0.010000 1.680000 ( 1.683415)
hash transpose 1.680000 0.010000 1.690000 ( 1.688110)
slice 1.800000 0.010000 1.810000 ( 1.816215)
Ayrıntılandırma şöyle görünecek:
module CoreExtensions
module Extractable
refine Hash do
def extract(*keys)
select { |k, _v| keys.include?(k) }
end
end
end
end
Ve kullanmak için:
using ::CoreExtensions::Extractable
{ a: 1, b: 2, c: 3 }.extract(:a, :b)
Hem delete_if
ve keep_if
Ruby iç parçasıdır. Burada, Hash
türü yamalamadan istediğinizi elde edebilirsiniz .
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h2 = h1.clone
p h1.keep_if { |key| [:b, :d, :e, :f].include?(key) } # => {:b => :B, :d => :D}
p h2.delete_if { |key, value| [:b, :d, :e, :f].include?(key) } #=> {:a => :A, :c => :C}
Daha fazla bilgi için, belgelerden aşağıdaki bağlantılara bakın:
Diğerlerinin de belirttiği gibi Ruby 2.5, Hash # dilim yöntemini ekledi.
Rails 5.2.0beta1 ayrıca, Ruby'nin önceki bir sürümünü kullanan çerçeve kullanıcıları için işlevselliği bozmak için kendi Hash # dilim sürümünü ekledi. https://github.com/rails/rails/commit/01ae39660243bc5f0a986e20f9c9bff312b1b5f8
Herhangi bir nedenle kendinizinkini uygulamak istiyorsanız, bu da güzel bir astar:
def slice(*keys)
keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
end unless method_defined?(:slice)
Bu kod, istediğiniz işlevselliği Hash sınıfına enjekte eder:
class Hash
def extract_subhash! *keys
to_keep = self.keys.to_a - keys
to_delete = Hash[self.select{|k,v| !to_keep.include? k}]
self.delete_if {|k,v| !to_keep.include? k}
to_delete
end
end
ve sağladığınız sonuçları üretir:
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
p h1.extract_subhash!(:b, :d, :e, :f) # => {b => :B, :d => :D}
p h1 #=> {:a => :A, :c => :C}
Not: Bu yöntem aslında çıkarılan anahtarları / değerleri döndürür.
Ruby 2.5'te çalışmıyorsanız ve yeni bir yöntem ekleyerek Hash sınıfınızı kirletmemek istemiyorsanız yararlı olabilecek işlevsel bir çözüm:
slice_hash = -> keys, hash { hash.select { |k, _v| keys.include?(k) } }.curry
Ardından iç içe geçmiş hash'lere bile uygulayabilirsiniz:
my_hash = [{name: "Joe", age: 34}, {name: "Amy", age: 55}]
my_hash.map(&slice_hash.([:name]))
# => [{:name=>"Joe"}, {:name=>"Amy"}]
Dilim yöntemine ek olarak, orijinal hash'den ayırmak istediğiniz subhash anahtarları dinamik olacaksa, beğenebilirsiniz,
slice(*dynamic_keys) # dynamic_keys should be an array type
Bunu, sadece çıkarmak istediğimiz anahtarlar üzerinde döngü yaparak ve sadece anahtarın var olup olmadığını kontrol ederek ve sonra onu çıkararak yapabiliriz.
class Hash
def extract(*keys)
extracted_hash = {}
keys.each{|key| extracted_hash[key] = self.delete(key) if self.has_key?(key)}
extracted_hash
end
end
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h2 = h1.extract(:b, :d, :e, :f)