如何从哈希中提取子哈希?

saw*_*awa 87 ruby hash

我有一个哈希:

h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
Run Code Online (Sandbox Code Playgroud)

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

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

ska*_*lee 130

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}
Run Code Online (Sandbox Code Playgroud)

注意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}
Run Code Online (Sandbox Code Playgroud)

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

  • `x.except!(:c,:d)`(带有bang)的结果应该是`#=> {:a => 1,:b => 2}`.好,如果你可以编辑你的答案. (3认同)

Gaz*_*ler 55

如果您特别希望该方法返回提取的元素但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} 
Run Code Online (Sandbox Code Playgroud)

如果你想将其修补到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
Run Code Online (Sandbox Code Playgroud)

如果您只想从哈希中删除指定的元素,那么使用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} 
Run Code Online (Sandbox Code Playgroud)

  • 虽然这个答案对于纯 ruby​​ 来说是不错的,但如果您使用的是 rails,那么下面的答案(使用内置的 `slice` 或 `except`,取决于你的需要)更简洁 (3认同)
  • 这是O(n2) - 你将在select上有一个循环,include上的另一个循环将被称为h1.size次. (2认同)

met*_*gfu 27

如果您使用rails,Hash#slice可行的方法.

{:a => :A, :b => :B, :c => :C, :d => :D}.slice(:a, :c)
# =>  {:a => :A, :c => :C}
Run Code Online (Sandbox Code Playgroud)

如果你不使用rails,Hash#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
Run Code Online (Sandbox Code Playgroud)

例如:

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

except({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2) 
# => {:foo => 'bar'}
Run Code Online (Sandbox Code Playgroud)

说明:

出于{: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}
Run Code Online (Sandbox Code Playgroud)

如果你觉得猴子补丁是要走的路,那么你想要的就是:

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
Run Code Online (Sandbox Code Playgroud)

  • Mokey修补绝对是去IMO的方式.更清洁,使意图更清晰. (2认同)

dhu*_*han 22

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}
Run Code Online (Sandbox Code Playgroud)


Rya*_*pte 5

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}
Run Code Online (Sandbox Code Playgroud)


小智 5

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

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

extracted_slice = initial_hash.slice!(:a, :c)
Run Code Online (Sandbox Code Playgroud)

initial_hash现在应该是

{:b => 2, :d =>4}
Run Code Online (Sandbox Code Playgroud)

extract_slide现在将是

{:a => 1, :c =>3}
Run Code Online (Sandbox Code Playgroud)

你可以看一下 slice.rb in ActiveSupport 3.1.3