Ruby 如何从哈希中提取子哈希?
我有一个杂烩:Ruby 如何从哈希中提取子哈希?,ruby,hash,Ruby,Hash,我有一个杂烩: h1 = {:a => :A, :b => :B, :c => :C, :d => :D} 提取这样的子散列的最佳方法是什么 h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D} h1 #=> {:a => :A, :c => :C} 如果您特别希望该方法返回提取的元素,但h1保持不变: h1 = {:a => :A, :b => :B,
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
提取这样的子散列的最佳方法是什么
h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D}
h1 #=> {:a => :A, :c => :C}
如果您特别希望该方法返回提取的元素,但h1保持不变:
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}
如果要将其修补到哈希类中:
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
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
如果您只想从散列中删除指定的元素,那么使用它会容易得多
此代码将您要求的功能注入哈希类:
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
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
并生成您提供的结果:
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}
注意:此方法实际上返回提取的键/值。您可以使用slice*ActiveSupport的核心扩展中提供的密钥
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}
initial_hash = {:a => 1, :b => 2, :c => 3, :d => 4}
extracted_slice = initial_hash.slice!(:a, :c)
最初的_散列现在是
{:b => 2, :d =>4}
{:a => 1, :c =>3}
这张幻灯片现在应该是
{:b => 2, :d =>4}
{:a => 1, :c =>3}
您可以查看ActiveSupport 3.1.3中的slice.rb,如果您使用rails,那么使用Hash.rb可能会很方便
h = {a:1, b:2}
h1 = h.except(:a) # {b:2}
至少从2.3.8开始,ActiveSupport就提供了四种方便的方法:slice,except和它们的破坏性对应方法:slice!除了!。在其他回答中提到了这些问题,但概括起来:
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方法的返回值。它们不仅将定制现有哈希,还将返回删除的未保留的条目。哈什除外!最适合问题中给出的示例:
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不需要整个Rails,它非常轻量级。事实上,很多非rails gem都依赖于它,所以很可能您已经在Gemfile.lock中有了它。不需要自己扩展Hash类。如果使用rails,这是一个不错的选择
{:a => :A, :b => :B, :c => :C, :d => :D}.slice(:a, :c)
# => {:a => :A, :c => :C}
如果不使用rails,则可以执行以下操作:
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
例:
说明:
在{:a=>1,:b=>2,:c=>3}中,我们想要{:a=>1,:b=>2}
如果您觉得猴子修补是一种方式,以下是您想要的:
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
下面是建议方法的快速性能比较,选择似乎是最快的
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)
优化将如下所示:
module CoreExtensions
module Extractable
refine Hash do
def extract(*keys)
select { |k, _v| keys.include?(k) }
end
end
end
end
使用它:
using ::CoreExtensions::Extractable
{ a: 1, b: 2, c: 3 }.extract(:a, :b)
增加:
如果您不是在Ruby 2.5上运行,并且不想通过添加新方法污染哈希类,那么下面是一个功能解决方案,它非常有用:
slice_hash = -> keys, hash { hash.select { |k, _v| keys.include?(k) } }.curry
然后,您甚至可以将其应用于嵌套哈希:
my_hash = [{name: "Joe", age: 34}, {name: "Amy", age: 55}]
my_hash.map(&slice_hash.([:name]))
# => [{:name=>"Joe"}, {:name=>"Amy"}]
delete_if和keep_if都是Ruby核心的一部分。在这里,您可以在不修补哈希类型的情况下实现所需的功能
h1={:a=>:a,:b=>:b,:c=>:c,:d=>:d}
h2=h1.5
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}
有关更多信息,请查看文档中的以下链接:
正如其他人提到的,Ruby 2.5添加了Hashslice方法 Rails 5.2.0beta1还添加了自己版本的Hashslice,为使用Ruby早期版本的框架用户填充功能。 如果出于任何原因希望实现您自己的,那么它也是一个不错的单行程序:
def slice(*keys)
keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
end unless method_defined?(:slice)
只是对slice方法的一个补充,如果要从原始散列中分离的子散列键是动态的,您可以这样做
slice(*dynamic_keys) # dynamic_keys should be an array type
我们可以通过循环我们只想提取的密钥来实现,只需检查密钥是否存在,然后提取它
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)
干得好。不完全是他想要的。你的方法返回:{:d=>:d,:b=>:b,:e=>nil,:f=>nil}{:c=>:c,:a=>:a,:d=>:d,:b=>:b}一个等价的一行,可能更快的解决方案:def subhash*键选择{k,v |键。include?k}尾端注意:@JanDvorak的可能重复这个问题不仅是关于返回subhash,还关于修改现有的。非常相似的事情,但是ActiveSupport有不同的方法来处理它们。我想你是在描述extract!。摘录从初始哈希中删除密钥,返回包含已删除密钥的新哈希。片执行相反的操作:再次从初始哈希中删除除指定键以外的所有键,返回包含已删除键的新哈希。切吧!更像是一个保留操作。ActiveSupport不是Ruby缝合的一部分这是在2-你将在select上有一个循环,在include上有另一个循环,称为h1.size times。虽然这个答案对于纯Ruby来说很合适,但如果你使用rails,下面的答案使用内置切片或except,根据您的需要,它会更干净。如果答案正确,请参见下文x.except的结果!:c、 带有bang的:d应该是=>{:a=>1,:b=>2}。如果你能编辑你的答案,那就好了。这应该是正确的答案。密钥修补绝对是IMO的一种方式。更清晰,更清晰。添加到修改代码以正确处理核心模块,定义模块并导入扩展哈希核心。。。模块CoreExtensions模块哈希定义切片*键::哈希[[键,self.values_at*键]。转置]结束哈希。包含CoreExtensions::哈希
slice(*dynamic_keys) # dynamic_keys should be an array type
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)