我有一个数组,我想制作一个哈希,所以我可以快速问"数组中的X是什么?".
在perl中,有一种简单(快速)的方法:
my @array = qw( 1 2 3 );
my %hash;
@hash{@array} = undef;
Run Code Online (Sandbox Code Playgroud)
这会生成一个如下所示的哈希:
{
1 => undef,
2 => undef,
3 => undef,
}
Run Code Online (Sandbox Code Playgroud)
我在Ruby中提出的最好的是:
array = [1, 2, 3]
hash = Hash[array.map {|x| [x, nil]}]
Run Code Online (Sandbox Code Playgroud)
这使:
{1=>nil, 2=>nil, 3=>nil}
Run Code Online (Sandbox Code Playgroud)
有更好的Ruby方式吗?
不,Array.include?不是个好主意.它很慢.它在O(n)中进行查询而不是O(1).为简洁起见,我的示例数组有三个元素; 假设实际的有一百万个元素.我们做一点基准测试:
#!/usr/bin/ruby -w
require 'benchmark'
array = (1..1_000_000).to_a
hash = Hash[array.map {|x| [x, nil]}]
Benchmark.bm(15) do |x|
x.report("Array.include?") { 1000.times { array.include?(500_000) } }
x.report("Hash.include?") { 1000.times { hash.include?(500_000) } }
end
Run Code Online (Sandbox Code Playgroud)
生产:
user system total real
Array.include? 46.190000 0.160000 46.350000 ( 46.593477)
Hash.include? 0.000000 0.000000 0.000000 ( 0.000523)
Run Code Online (Sandbox Code Playgroud)
ram*_*ion 43
如果您只需要哈希是成员身份,请考虑使用Set:
组
Set实现了无序值的集合,没有重复.这是Array直观的互操作设施和Hash快速查找的混合体.
Set易于与Enumerable对象一起使用(实现
each).除了集合和数组之外,大多数初始化方法和二元运算符都接受通用的Enumerable对象.可以使用该 方法将 Enumerable对象转换为Setto_set.Set使用Hash作为存储,因此您必须注意以下几点:
- 元素的平等根据
Object#eql?和确定Object#hash.- Set假定每个元素的标识在存储时不会更改.修改集合的元素会将集合呈现为不可靠状态.
- 当要存储字符串时,将存储字符串的冻结副本,除非原始字符串已被冻结.
对照
比较运营商
<,>,<=和>=被实现为简写{正确_,} {子集?,集?}方法.但是,<=>故意将 操作员遗漏,因为不是每一对都是可比较的.(例如{x,y}与{x,z})例
Run Code Online (Sandbox Code Playgroud)require 'set' s1 = Set.new [1, 2] # -> #<Set: {1, 2}> s2 = [1, 2].to_set # -> #<Set: {1, 2}> s1 == s2 # -> true s1.add("foo") # -> #<Set: {1, 2, "foo"}> s1.merge([2, 6]) # -> #<Set: {1, 2, "foo", 6}> s1.subset? s2 # -> false s2.subset? s1 # -> true[...]
公共类方法
new(enum = nil)
创建一个包含给定可枚举对象元素的新集合.
如果给出了块,则枚举元素由给定块预处理.
edx*_*edx 22
试试这个:
a=[1,2,3]
Hash[a.zip]
Run Code Online (Sandbox Code Playgroud)
vie*_*bel 14
你可以做这个非常方便的技巧:
Hash[*[1, 2, 3, 4].map {|k| [k, nil]}.flatten]
=> {1=>nil, 2=>nil, 3=>nil, 4=>nil}
Run Code Online (Sandbox Code Playgroud)
如果你想快速询问"数组中的X是什么?" 你应该使用Array#include?.
编辑(响应OP中的添加):
如果您想要快速查找时间,请使用Set.拥有指向所有nils 的哈希是愚蠢的.转换也是一个简单的过程Array#to_set.
require 'benchmark'
require 'set'
array = (1..1_000_000).to_a
set = array.to_set
Benchmark.bm(15) do |x|
x.report("Array.include?") { 1000.times { array.include?(500_000) } }
x.report("Set.include?") { 1000.times { set.include?(500_000) } }
end
Run Code Online (Sandbox Code Playgroud)
在我的机器上的结果:
user system total real
Array.include? 36.200000 0.140000 36.340000 ( 36.740605)
Set.include? 0.000000 0.000000 0.000000 ( 0.000515)
Run Code Online (Sandbox Code Playgroud)
您应该考虑仅使用一个集合而不是数组,以便永远不需要转换.
小智 6
我很确定没有一个聪明的方法来构造这个哈希.我倾向于明确并陈述我正在做的事情:
hash = {}
array.each{|x| hash[x] = nil}
Run Code Online (Sandbox Code Playgroud)
它看起来并不是特别优雅,但它很清楚,并且完成了工作.
FWIW,你的原始建议(至少在Ruby 1.8.6下)似乎不起作用.我得到一个"ArgumentError:Hash的奇数个参数"错误.Hash.[]需要一个文字的,偶数加长的值列表:
Hash[a, 1, b, 2] # => {a => 1, b => 2}
Run Code Online (Sandbox Code Playgroud)
所以我尝试将您的代码更改为:
hash = Hash[*array.map {|x| [x, nil]}.flatten]
Run Code Online (Sandbox Code Playgroud)
但表现可怕:
#!/usr/bin/ruby -w
require 'benchmark'
array = (1..100_000).to_a
Benchmark.bm(15) do |x|
x.report("assignment loop") {hash = {}; array.each{|e| hash[e] = nil}}
x.report("hash constructor") {hash = Hash[*array.map {|e| [e, nil]}.flatten]}
end
Run Code Online (Sandbox Code Playgroud)
给
user system total real
assignment loop 0.440000 0.200000 0.640000 ( 0.657287)
hash constructor 4.440000 0.250000 4.690000 ( 4.758663)
Run Code Online (Sandbox Code Playgroud)
除非我在这里遗漏了一些东西,否则一个简单的赋值循环似乎是构造这个哈希的最清晰,最有效的方法.
Rampion打败了我.设置可能就是答案.
你可以做:
require 'set'
set = array.to_set
set.include?(x)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
50816 次 |
| 最近记录: |