【问题标题】:Finding all key from one hash, in an array of hashes在散列数组中从一个散列中查找所有键
【发布时间】:2015-04-16 06:26:32
【问题描述】:

我有一个哈希,我需要将其与哈希数组进行比较。

例如:

# Array of Hashes to compare another hash against
@stored_hash = [
   {:a => 1, :b => 2, :c => {:d => 3, :e => 4, :f => 5}},
   {:a => 1, :b => 2, :c => {:d => 3, :e => 4, :f => 5}},
   {:a => 1, :b => 2, :c => {:d => 3, :e => 4, :f => 5}},
]

# Hash to compare to Array of Hashes
@sample_hash = {:d => 3, :e => 4, :f => 5}

我正在寻找来自@sample_hash 的与来自所有@stored_hashes 的任何键相匹配的键

我可以像这样将单个哈希与另一个哈希进行比较

res = @sample_hash.keys.select { |k| @stored_hash.key?(k) }

但我无法让它适用于哈希数组。帮助表示赞赏。

编辑

这是我迄今为止尝试过的。

res = @sample_hash.keys.select { |k| @stored_hash.map(&:keys).include?(k) }

这似乎只是让程序停止(我正在处理一个非常大的数据集)

我也试过这个:

res = @sample_hash.keys.select { |k| @stored_hash.map { |x| x.key?(k) } }

返回结果,但如果我做相反的事情:

res = @sample_hash.keys.reject { |k| @stored_hash.map { |x| x.key?(k) } }

我没有得到任何结果(我知道哈希中既有公共键也有非公共键)

【问题讨论】:

  • 您能否向我们展示您的尝试,以便我们帮助您编写代码?
  • @SimoneCarletti 我对我的问题进行了编辑。
  • 也许您可以为示例数据添加一些预期的输出?

标签: ruby


【解决方案1】:

我们可以分两步完成。

第 1 步

stored_hash = [
   { :a => 1, :b => 2, :c => { :d => 3, :e => 4, :f => 5 } },
   { :a => 1, :b => 2, :c => { :d => 3, :e => 4, :g => 5 } },
   { :a => 1, :b => 2, :c => { :d => 3, :e => 4, :h => 5 } },
]

请注意,我对stored_hash 进行了一些更改。

def all_keys_all_hashes(arr)
  arr.each_with_object([]) { |h,keys| keys.concat(all_keys(h)) }.uniq
end

def all_keys(h)
  h.each_with_object([]) do |(k,v),arr|
    arr << k
    arr.concat(all_keys(v)) if v.is_a? Hash
  end
end

keys = all_keys_all_hashes(stored_hash)
  #=> [:a, :b, :c, :d, :e, :f, :g, :h]

第 2 步

sample_hash = {:d => 3, :e => 4, :f => 5, :z => 99 }

sample_hash.keys & keys
  #=> [:d, :e, :f] 

附录

如果您遇到像这样的一次性任务问题,这里有一种“快速而肮脏”的方法来获取stored_hash 中的所有键,前提是它们都是符号、字符串或自然数。做法是把数组转成字符串,然后用正则表达式提取key,再做一点处理。

首先,为了处理更一般的情况,让我们修改stored_hash的前两个元素中的每个元素中的第一个键:

stored_hash = [
   {"a" => 1, :b => 2, :c => {:d => 3, :e => 4, :f => 5}},
   { 99 => 1, :b => 2, :c => {:d => 3, :e => 4, :f => 5}},
   { :a => 1, :b => 2, :c => {:d => 3, :e => 4, :f => 5}},
]

你可以这样做:

stored_hash.to_s.scan(/[{,]\s*\K[^{,]+?(?==>)/).uniq.map do |s|
  case
  when (s[0] == ?:)   then s[1..-1].to_sym
  when (s =~ /^\d+$/) then s.to_i
  else s.gsub(?", '')
  end
end
  #=> ["a", :b, :c, :d, :e, :f, 99, :a] 

\K 指示 Ruby 匹配之前的内容,但不将其包含在它保留的匹配中。当前面的匹配是可变长度的 (\s*) 时,它特别方便,因为正向回溯不能用于可变长度匹配。

这不能正确处理带引号的符号(例如,:"text"),但修改正则表达式来处理这种情况并不难。

步骤如下:

str = stored_hash.to_s
  #=> "[{\"a\"=>1, :b=>2, :c=>{:d=>3, :e=>4, :f=>5}}, " +
  #    "{99=>1, :b=>2, :c=>{:d=>3, :e=>4, :f=>5}}, " +
  #    "{:a=>1, :b=>2, :c=>{:d=>3, :e=>4, :f=>5}}]"

(我已将字符串分成三部分,因此无需使用水平滚动即可阅读。)

a1 = str.scan(/[{,]\s*\K[^{,]+?(?==>)/)
  #=> ["\"a\"", ":b", ":c", ":d", ":e", ":f",
  #    "99", ":b", ":c", ":d", ":e", ":f",
  #    ":a", ":b", ":c", ":d", ":e", ":f"]
a2 = a1.uniq
  #=> ["\"a\"", ":b", ":c", ":d", ":e", ":f", "99", ":a"]
a2.map do |s|
  case
  when (s[0] == ?:)   then s[1..-1].to_sym
  when (s =~ /^\d+$/) then s.to_i
  else s.gsub(?", '')
  end
end
  #=> ["a", :b, :c, :d, :e, :f, 99, :a]

【讨论】:

  • 是否也可以获取所有非公用密钥?
  • 那就是keys - sample_hash.keys #=&gt; [:a, :b, :c, :g, :h]
  • 我误解了您所说的“非公用键”是什么意思。看来你想要sample_hash.keys - keys #=&gt; [:z]
猜你喜欢
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 2018-07-27
  • 1970-01-01
  • 2014-09-13
  • 2011-01-15
  • 2021-02-04
  • 2011-05-08
相关资源
最近更新 更多