ruby 如何过滤散列数组以仅获取另一个数组中的键?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9538090/
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:51:03  来源:igfitidea点击:

How can I filter an array of hashes to get only the keys in another array?

rubyarrayshash

提问by pedalpete

I'm trying get a subset of keys for each hash in an array.

我正在尝试为数组中的每个散列获取一个键子集。

The hashes are actually much larger, but I figured this is easier to understand:

散列实际上要大得多,但我认为这更容易理解:

[
  {
    id:2,
    start: "3:30",
    break: 30,
    num_attendees: 14
  },
  {
    id: 3,
    start: "3: 40",
    break: 40,
    num_attendees: 4
  },
  {
    id: 4,
    start: "4: 40",
    break: 10,
    num_attendees: 40
  }
]

I want to get only the idand startvalues.

我只想得到idstart值。

I've tried:

我试过了:

return_keys = ['id','start']
return_array = events.select{|key,val|  key.to_s.in? return_keys}

but this returns an empty array.

但这会返回一个空数组。

回答by Andrew Marshall

This should do what you want:

这应该做你想做的:

events.map do |hash|
  hash.select do |key, value|
    [:id, :start].include? key
  end
end

Potentially faster (but somewhat less pretty) solution:

可能更快(但不太漂亮)的解决方案:

events.map do |hash|
  { id: hash[:id], start: hash[:start] }
end

If you need return_keysto be dynamic:

如果您需要return_keys动态:

return_keys = [:id, :start]
events.map do |hash|
  {}.tap do |new_hash|
    return_keys.each do |key|
      new_hash[key] = hash[key]
    end
  end
end

Note that, in your code, selectpicks out elements from the array, since that's what you called it on, but doesn't change the hashes contained within the array.

请注意,在您的代码中,selectarray 中挑选元素,因为这是您调用它的原因,但不会更改包含在数组中的哈希值。

If you're concerned about performance, I've benchmarked all of the solutions listed here (code):

如果您担心性能,我已经对此处列出的所有解决方案进行了基准测试(代码):

                user     system      total        real
amarshall 1  0.140000   0.000000   0.140000 (  0.140316)
amarshall 2  0.060000   0.000000   0.060000 (  0.066409)
amarshall 3  0.100000   0.000000   0.100000 (  0.101469)
tadman 1     0.140000   0.010000   0.150000 (  0.145489)
tadman 2     0.110000   0.000000   0.110000 (  0.111838)
mu           0.130000   0.000000   0.130000 (  0.128688)

回答by mu is too short

If you happen to be using Rails (or don't mind pulling in all or part of ActiveSupport) then you could use Hash#slice:

如果您碰巧正在使用 Rails(或者不介意使用全部或部分 ActiveSupport),那么您可以使用Hash#slice

return_array = events.map { |h| h.slice(:id, :start) }

Hash#slicedoes some extra work under the covers but it is probably fast enough that you won't notice it for small hashes and the clarity is quite nice.

Hash#slice在幕后做了一些额外的工作,但它可能足够快,以至于您不会注意到小哈希值,并且清晰度非常好。

回答by tadman

A better solution is to use a hash as your index instead of doing a linear array lookup for each key:

更好的解决方案是使用散列作为索引,而不是对每个键进行线性数组查找:

events = [{id:2, start:"3:30",break:30,num_attendees:14},{id:3, start:"3:40",break:40,num_attendees:4},{id:4, start:"4:40",break:10,num_attendees:40}]

return_keys = [ :id, :start ]

# Compute a quick hash to extract the right values: { key => true }
key_index = Hash[return_keys.collect { |key| [ key, true ] }]

return_array = events.collect do |event|
  event.select do |key, value|
    key_index[key]
  end
end

# => [{:id=>2, :start=>"3:30"}, {:id=>3, :start=>"3:40"}, {:id=>4, :start=>"4:40"}]

I've adjusted this to use symbols as the key names to match your definition of events.

我已经对此进行了调整,以使用符号作为键名来匹配您对events.

This can be further improved by using the return_keysas a direct driver:

这可以通过将return_keys用作直接驱动程序来进一步改进:

events = [{id:2, start:"3:30",break:30,num_attendees:14},{id:3, start:"3:40",break:40,num_attendees:4},{id:4, start:"4:40",break:10,num_attendees:40}]

return_keys = [ :id, :start ]

return_array = events.collect do |event|
  Hash[
    return_keys.collect do |key|
      [ key, event[key] ]
    end
  ]
end

The result is the same. If the subset you're extracting tends to be much smaller than the original, this might be the best approach.

结果是一样的。如果您提取的子集往往比原始子集小得多,这可能是最好的方法。

回答by Cary Swoveland

Considering that efficiency appears to be a concern, I would suggest the following.

考虑到效率似乎是一个问题,我建议如下。

Code

代码

require 'set'

def keep_keys(arr, keeper_keys)
  keepers = keeper_keys.to_set
  arr.map { |h| h.select { |k,_| keepers.include?(k) } }
end

This uses Hash#select, which, unlike Enumerable#select, returns a hash. I've converted keeper_keysto a set for fast lookups.

这使用Hash#select,与Enumerable#select不同,它返回一个哈希值。我已转换keeper_keys为一组以进行快速查找。

Examples

例子

arr = [{ id:2, start: "3:30", break: 30 },
       { id: 3, break: 40, num_attendees: 4 },
       { break: 10, num_attendees: 40 }]

keep_keys arr, [:id, :start]
  #=> [{:id=>2, :start=>"3:30"}, {:id=>3}, {}] 
keep_keys arr, [:start, :break]
  #=> [{:start=>"3:30", :break=>30}, {:break=>40}, {:break=>10}] 
keep_keys arr, [:id, :start, :cat]
  #=> [{:id=>2, :start=>"3:30"}, {:id=>3}, {}] 
keep_keys arr, [:start]
  #=> [{:start=>"3:30"}, {}, {}] 
keep_keys arr, [:cat, :dog]