当前位置:  开发笔记 > 编程语言 > 正文

在Ruby中将数组转换为索引哈希

如何解决《在Ruby中将数组转换为索引哈希》经验,为你挑选了6个好方法。

我有一个数组,我想制作一个哈希,所以我可以快速问"数组中的X是什么?".

在perl中,有一种简单(快速)的方法:

my @array = qw( 1 2 3 );
my %hash;
@hash{@array} = undef;

这会生成一个如下所示的哈希:

{
    1 => undef,
    2 => undef,
    3 => undef,
}

我在Ruby中提出的最好的是:

array = [1, 2, 3]
hash = Hash[array.map {|x| [x, nil]}]

这使:

{1=>nil, 2=>nil, 3=>nil}

有更好的Ruby方式吗?

编辑1

不,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

生产:

                     user     system      total        real
Array.include?  46.190000   0.160000  46.350000 ( 46.593477)
Hash.include?    0.000000   0.000000   0.000000 (  0.000523)

rampion.. 43

如果您只需要哈希是成员身份,请考虑使用Set:

Set实现了无序值的集合,没有重复.这是Array直观的互操作设施和Hash快速查找的混合体.

Set易于与Enumerable对象一起使用(实现 each).除了集合和数组之外,大多数初始化方法和二元运算符都接受通用的Enumerable对象.可以使用该 方法将 Enumerable对象转换为Setto_set.

Set使用Hash作为存储,因此您必须注意以下几点:

元素的平等根据Object#eql?和确定Object#hash.

Set假定每个元素的标识在存储时不会更改.修改集合的元素会将集合呈现为不可靠状态.

当要存储字符串时,将存储字符串的冻结副本,除非原始字符串已被冻结.

对照

比较运营商<,>,<=>=被实现为简写{正确_,} {子集?,集?}方法.但是,<=>故意将 操作员遗漏,因为不是每一对都是可比较的.(例如{x,y}与{x,z})

require 'set'
s1 = Set.new [1, 2]                   # -> #
s2 = [1, 2].to_set                    # -> #
s1 == s2                              # -> true
s1.add("foo")                         # -> #
s1.merge([2, 6])                      # -> #
s1.subset? s2                         # -> false
s2.subset? s1                         # -> true

[...]

公共类方法

new(enum = nil)

创建一个包含给定可枚举对象元素的新集合.

如果给出了块,则枚举元素由给定块预处理.


edx.. 22

试试这个:

a=[1,2,3]
Hash[a.zip]

`Hash [a.zip]`也返回相同的响应. (4认同)


viebel.. 14

你可以做这个非常方便的技巧:

Hash[*[1, 2, 3, 4].map {|k| [k, nil]}.flatten]
=> {1=>nil, 2=>nil, 3=>nil, 4=>nil}


Zach Langley.. 9

如果你想快速询问"数组中的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

在我的机器上的结果:

                     user     system      total        real
Array.include?  36.200000   0.140000  36.340000 ( 36.740605)
Set.include?     0.000000   0.000000   0.000000 (  0.000515)

您应该考虑仅使用一个集合而不是数组,以便永远不需要转换.



1> rampion..:

如果您只需要哈希是成员身份,请考虑使用Set:

Set实现了无序值的集合,没有重复.这是Array直观的互操作设施和Hash快速查找的混合体.

Set易于与Enumerable对象一起使用(实现 each).除了集合和数组之外,大多数初始化方法和二元运算符都接受通用的Enumerable对象.可以使用该 方法将 Enumerable对象转换为Setto_set.

Set使用Hash作为存储,因此您必须注意以下几点:

元素的平等根据Object#eql?和确定Object#hash.

Set假定每个元素的标识在存储时不会更改.修改集合的元素会将集合呈现为不可靠状态.

当要存储字符串时,将存储字符串的冻结副本,除非原始字符串已被冻结.

对照

比较运营商<,>,<=>=被实现为简写{正确_,} {子集?,集?}方法.但是,<=>故意将 操作员遗漏,因为不是每一对都是可比较的.(例如{x,y}与{x,z})

require 'set'
s1 = Set.new [1, 2]                   # -> #
s2 = [1, 2].to_set                    # -> #
s1 == s2                              # -> true
s1.add("foo")                         # -> #
s1.merge([2, 6])                      # -> #
s1.subset? s2                         # -> false
s2.subset? s1                         # -> true

[...]

公共类方法

new(enum = nil)

创建一个包含给定可枚举对象元素的新集合.

如果给出了块,则枚举元素由给定块预处理.



2> edx..:

试试这个:

a=[1,2,3]
Hash[a.zip]


`Hash [a.zip]`也返回相同的响应.

3> viebel..:

你可以做这个非常方便的技巧:

Hash[*[1, 2, 3, 4].map {|k| [k, nil]}.flatten]
=> {1=>nil, 2=>nil, 3=>nil, 4=>nil}



4> Zach Langley..:

如果你想快速询问"数组中的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

在我的机器上的结果:

                     user     system      total        real
Array.include?  36.200000   0.140000  36.340000 ( 36.740605)
Set.include?     0.000000   0.000000   0.000000 (  0.000515)

您应该考虑仅使用一个集合而不是数组,以便永远不需要转换.



5> 小智..:

我很确定没有一个聪明的方法来构造这个哈希.我倾向于明确并陈述我正在做的事情:

hash = {}
array.each{|x| hash[x] = nil}

它看起来并不是特别优雅,但它很清楚,并且完成了工作.

FWIW,你的原始建议(至少在Ruby 1.8.6下)似乎不起作用.我得到一个"ArgumentError:Hash的奇数个参数"错误.Hash.[]需要一个文字的,偶数加长的值列表:

Hash[a, 1, b, 2] # => {a => 1, b => 2}

所以我尝试将您的代码更改为:

hash = Hash[*array.map {|x| [x, nil]}.flatten]

但表现可怕:

#!/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

                     user     system      total        real
assignment loop  0.440000   0.200000   0.640000 (  0.657287)
hash constructor  4.440000   0.250000   4.690000 (  4.758663)

除非我在这里遗漏了一些东西,否则一个简单的赋值循环似乎是构造这个哈希的最清晰,最有效的方法.



6> mtyaka..:

Rampion打败了我.设置可能就是答案.

你可以做:

require 'set'
set = array.to_set
set.include?(x)

推荐阅读
mobiledu2402851373
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有