从数组创建哈希表的最简洁方法

33

我似乎经常遇到这个问题。 我需要使用数组中每个对象的属性作为键来构建哈希表。

假设我需要一个用其ID作为键的ActiveRecord对象示例的哈希表 通常的方法:

ary = [collection of ActiveRecord objects]
hash = ary.inject({}) {|hash, obj| hash[obj.id] = obj }

另一种方法:

ary = [collection of ActiveRecord objects]
hash = Hash[*(ary.map {|obj| [obj.id, obj]}).flatten]

梦想之路: 我可以自己创建这个功能,但是在Ruby或Rails中有没有类似的东西呢?

ary = [collection of ActiveRecord objects]
hash = ary.to_hash &:id
#or at least
hash = ary.to_hash {|obj| obj.id}
5个回答

67

ActiveSupport中已经有一个方法可以实现这个功能。

['an array', 'of active record', 'objects'].index_by(&:id)

仅供参考,这里是实现代码:

def index_by
  inject({}) do |accum, elem|
    accum[yield(elem)] = elem
    accum
  end
end

如果你非常渴望写成一行,可以将其重构为:

def index_by
  inject({}) {|hash, elem| hash.merge!(yield(elem) => elem) }
end

1
我认为如果你将merge更改为merge!,你就可以避免创建许多不必要的中间哈希。 - Scott
如果您在应用程序的关键路径中要多次使用此功能,您可能需要考虑使用ary.index_by{|o| o.id}而不是使用symbol_to_proc。 - krusty.ar
index_by 似乎是 Ruby 的 group_by 的重复。我有什么遗漏吗? - JMH
2
group_by的值是一个数组,而index_by则假定每个键只有一个项目,因此该值是单个项目,而不是数组。 - August Lilleaas
在Rails 4.2.5和Ruby 2.3.0中,['an array', 'of active record', 'objects'].index_by(&:id)会失败并显示错误NoMethodError: undefined method `id' for "an array":String。 - Sven R.
这个例子不是自包含的,字符串表示的是活动记录对象,它们有一个id方法,正如它们的内容所示 :) - August Lilleaas

10

最短的一个?

# 'Region' is a sample class here
# you can put 'self.to_hash' method into any class you like 

class Region < ActiveRecord::Base
  def self.to_hash
    Hash[*all.map{ |x| [x.id, x] }.flatten]
  end
end

9
在某些情况下,可能会得到一个普通的数组。
arr = ["banana", "apple"]
Hash[arr.map.with_index.to_a]
 => {"banana"=>0, "apple"=>1}

5

你可以自己添加 to_hash 方法到数组中。

class Array
  def to_hash(&block)
    Hash[*self.map {|e| [block.call(e), e] }.flatten]
  end
end

ary = [collection of ActiveRecord objects]
ary.to_hash do |element|
  element.id
end

0

2
我不建议为一个简单的方法添加依赖。 - ricks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接