ruby 如何从散列中提取子散列?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9025277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:42:57  来源:igfitidea点击:

How do I extract a sub-hash from a hash?

rubyhash

提问by sawa

I have a hash:

我有一个哈希:

h1 = {:a => :A, :b => :B, :c => :C, :d => :D}

What is the best way to extract a sub-hash like this?

提取这样的子哈希的最佳方法是什么?

h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D}
h1 #=> {:a => :A, :c => :C}

采纳答案by Gazler

If you specifically want the method to return the extracted elements but h1 to remain the same:

如果您特别希望该方法返回提取的元素但 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} 

And if you want to patch that into the Hash class:

如果您想将其修补到 Hash 类中:

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

If you just want to remove the specified elements from the hash, that is much easier using delete_if.

如果您只想从哈希中删除指定的元素,那么使用delete_if会容易得多。

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} 

回答by skalee

ActiveSupport, at least since 2.3.8, provides four convenient methods: #slice, #exceptand their destructive counterparts: #slice!and #except!. They were mentioned in other answers, but to sum them in one place:

ActiveSupport,至少从 2.3.8 开始,提供了四种方便的方法:#slice#except以及它们的破坏性对应方法:#slice!#except!。他们在其他答案中被提及,但将它们总结在一个地方:

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}

Note the return values of the bang methods. They will not only tailor existing hash but also return removed (not kept) entries. The Hash#except!suits best the example given in the question:

注意 bang 方法的返回值。他们不仅会定制现有的散列,还会返回已删除(未保留)的条目。最Hash#except!适合问题中给出的示例:

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}

ActiveSupportdoes not require whole Rails, is pretty lightweight. In fact, a lot of non-rails gems depend on it, so most probably you already have it in Gemfile.lock. No need to extend Hash class on your own.

ActiveSupport不需要整个 Rails,非常轻量级。事实上,很多非 Rails gem 都依赖于它,所以很可能你已经在 Gemfile.lock 中拥有它。无需自己扩展 Hash 类。

回答by metakungfu

If you use rails, Hash#sliceis the way to go.

如果您使用 railsHash#slice是要走的路。

{:a => :A, :b => :B, :c => :C, :d => :D}.slice(:a, :c)
# =>  {:a => :A, :c => :C}

If you don't use rails, Hash#values_at will return the values in the same order as you asked themso you can do this:

如果您不使用 railsHash#values_at 将按照您询问的顺序返回值,以便您可以这样做:

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

ex:

前任:

slice({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2) 
# => {'bar' => 'foo', 2 => 'two'}

except({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2) 
# => {:foo => 'bar'}

Explanation:

解释:

Out of {:a => 1, :b => 2, :c => 3}we want {:a => 1, :b => 2}

出于{:a => 1, :b => 2, :c => 3}我们想要{: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}

If you feels like monkey patching is the way to go, following is what you want:

如果你觉得猴子补丁是要走的路,下面是你想要的:

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

回答by dhulihan

Ruby 2.5added Hash#slice:

Ruby 2.5添加了Hash#slice

h = { a: 100, b: 200, c: 300 }
h.slice(:a)           #=> {:a=>100}
h.slice(:b, :c, :d)   #=> {:b=>200, :c=>300}

回答by Vijay

You can use slice!(*keys) which is available in the core extensions of ActiveSupport

您可以使用在 ActiveSupport 的核心扩展中可用的 slice!(*keys)

initial_hash = {:a => 1, :b => 2, :c => 3, :d => 4}

extracted_slice = initial_hash.slice!(:a, :c)

initial_hash would now be

initial_hash 现在是

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

extracted_slide would now be

extract_slide 现在将是

{:a => 1, :c =>3}

You can look at slice.rb in ActiveSupport 3.1.3

你可以看看 slice.rb in ActiveSupport 3.1.3

回答by Ryan LeCompte

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}

回答by Cary Swoveland

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}

回答by gayavat

if you use rails, it may be convenient to use Hash.except

如果你使用 rails,使用 Hash.except 可能会很方便

h = {a:1, b:2}
h1 = h.except(:a) # {b:2}

回答by Mark

Both delete_ifand keep_ifare part of Ruby core. Here you can achieve what you would like to without patching the Hashtype.

这两个delete_ifkeep_if是Ruby的核心的一部分。在这里,您可以在不修补Hash类型的情况下实现您想要的。

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}

For futher info, check the links below from the documentation:

有关更多信息,请查看文档中的以下链接:

回答by josh

As others have mentioned, Ruby 2.5 added the Hash#slice method.

正如其他人提到的,Ruby 2.5 添加了 Hash#slice 方法。

Rails 5.2.0beta1 also added it's own version of Hash#slice to shim the functionality for users of the framework that are using an earlier version of Ruby. https://github.com/rails/rails/commit/01ae39660243bc5f0a986e20f9c9bff312b1b5f8

Rails 5.2.0beta1 还添加了它自己的 Hash#slice 版本,以填充使用早期 Ruby 版本的框架用户的功能。 https://github.com/rails/rails/commit/01ae39660243bc5f0a986e20f9c9bff312b1b5f8

If looking to implement your own for whatever reason, it's a nice one liner as well:

如果出于某种原因想要实现自己的,它也是一个不错的单线:

 def slice(*keys)
   keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
 end unless method_defined?(:slice)