首页 > 解决方案 > 在哈希中选择某些键

问题描述

我有一个 Hashh并且想要拥有这些键的数组,其中它们的值满足特定条件。我天真的方法是:

result = h.select { |_, val| val.fulfils_condition? }.keys

这当然可行,但对我来说似乎效率低下,因为它需要构造一个中间哈希,然后从中计算结果数组。

当然我可以做一个显式循环:

result = []
h.each do
  |key, val|
  result << key if val.fulfils_condition?
end

但是由于对result. 我也在考虑这个:

result = h.reduce([]) do
  |memo, pair| 
  memo << pair.first if pair.last.fulfils_condition?
  memo
end

但这并不是真正更具可读性,并且需要构建中间pair数组,每个数组都包含一个键值对。

有没有替代的方法,既紧凑,又不需要计算临时哈希?

标签: ruby

解决方案


鉴于:

h = {}; (1..100).each {|v| h[v.to_s] = v }

您可以使用 memory_profiler gem 来测量分配。使用类似的东西MemoryProfiler.report { code }.total_allocated

如果内存分配在这里真的很重要,那么您预先分配结果然后枚举的#each方法就是您想要的。这样做的原因是 Ruby优化了 Hash#each 块的数量为 2,因此不会在每个循环中构造一个新数组。这种方法中唯一的分配是结果数组。

MemoryProfiler.report { r = [] ; h.each {|k, v| r << k if v > 1 } }.total_allocated
# => 1

使用#reduceOTOH 会导致每个循环分配一次,因为您违反了 arity 规则:

MemoryProfiler.report { h.reduce([]) {|agg, (k, v)| agg << k if v > 1 ; agg } }.total_allocated
# => 101

如果您想要更“独立”的东西并且愿意牺牲额外的分配,您将需要使用#each_key(它确实创建了一个中间键数组)然后索引到哈希中以测试每个值。

h.each_key.select {|k| h[k] > 1 }

推荐阅读