【问题标题】:How can I filter an array of hashes to get only the keys in another array?如何过滤散列数组以仅获取另一个数组中的键?
【发布时间】:2012-03-21 05:44:27
【问题描述】:

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

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

[
  {
    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
  }
]

我只想获取 id 和 start 值。

我试过了:

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

但这会返回一个空数组。

【问题讨论】:

    标签: ruby arrays hash


    【解决方案1】:

    这应该做你想做的:

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

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

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

    如果您需要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
    

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

    如果您担心性能,我已经对此处列出的所有解决方案 (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)
    

    【讨论】:

    • 对于events 中的 N 个键和每个哈希中的 M 个键,以及内部数组中的 P 个键,这以 O(MNP) 的速度执行,这可能会非常严重慢。
    • @tadman 虽然,我想这真的是 O(NP)?我认为没有比这更快的了。假设 P 非常小,它不应该真正影响时间复杂度。
    • 我还更新了代码,以便在 return_keys 需要动态时包含代码。
    • 真棒安德鲁,#2明显更快,我不认为代码特别不漂亮。我现在不需要返回键是动态的,而且哈希值可能会很大,所以我会选择 2 号门。
    • 干得好。如果选择的键很小且可预测,则 #2 是最佳解决方案。如果所涉及的数字变大,这些可能具有截然不同的属性,例如N=10e6, M=100, P=50,但如果已知值很小,这只是学术考虑。
    【解决方案2】:

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

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

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

    【讨论】:

    • 实际上,如果您不在 Rails 中,则需要 require 'active_support/core_ext'。核心扩展需要显式加载,所以 require 'active_support' 不起作用。 (我之所以这么说是因为后者是大多数人认为“拉动所有 ActiveSupport”的原因。)
    【解决方案3】:

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

    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"}]
    

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

    这可以通过使用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
    

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

    【讨论】:

    • 如果您好奇,我在这里对所有解决方案进行了基准测试,并将结果发布在我的答案 :)。
    【解决方案4】:

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

    代码

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

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

    示例

    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]
    

    【讨论】:

      猜你喜欢
      • 2021-12-22
      • 2020-12-13
      • 2011-07-26
      • 1970-01-01
      • 1970-01-01
      • 2014-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多