您可以通过多种方式做到这一点。
创建一个数组并将其转换为哈希
直到最近,您还可以使用公共类方法Hash::[] 将数组转换为散列。它的工作原理是这样的:
h = Hash[ [[:a, 1], [:b, 2]] ]
#=> {:a=>1, :b=>2}
或
h = Hash[:a, 1, :b, 2]
#=> {:a=>1, :b=>2}
在 Ruby v2.1.0 中引入了 Array#to_h 和 Enumerable#to_h 方法。第一个是这样的:
h = [[:a, 1], [:b, 2]].to_h
#=> {:a=>1, :b=>2}
因此,要使用Hash 或to_h,您必须先创建数组:
arr1 = [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
或
arr2 = ["Aqua", 0, "Blue", 1, "Green", 2, "Red", 3, "Yellow", 4]
在第二种情况下,我们会这样使用它:
Hash[*arr2]
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
让我们首先创建arr1。你是对的,你需要使用Enumerable#each_with_index。然后您需要使用Enumerable#to_a 将items 的每个元素转换为数组[<color>, index]。
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
arr = items.each_with_index.to_a
#=> [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
让我们更仔细地看一下:
enum = items.each_with_index
#=> #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]:each_with_index>
enum,一个枚举器,是类Enumerator 的一个实例。 Enumerator 类是includes Enumerable 模块的众多类之一,其中to_a 是一个实例方法。不仅如此:
arr = enum.to_a
#=> [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
将枚举器转换为所需的数组,但这是查看任何枚举器元素的便捷方法(通常传递给块或另一个枚举器)。
所以我们现在可以创建哈希了:
h = Hash[arr]
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
或
h = Hash[*arr.flatten]
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
或
h = arr.to_h
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
现在假设我们有:
items = ["Aqua", "Blue", "Green", "Aqua", "Aqua"]
然后我们得到:
items.each_with_index.to_a.to_h
#=> {"Aqua"=>4, "Blue"=>1, "Green"=>2}
在构建散列时,Ruby 首先创建键值对"Aqua"=>0,然后用"Aqua"=>3 覆盖,然后用"Aqua"=>4 覆盖。这是哈希具有唯一键这一事实的结果。
从头开始构建哈希
现在假设我们从一个空哈希开始:
h = {}
(同h = Hash.new)并添加键值对:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
items.each_index { |i| h[items[i]] = i }
#=> ["Aqua", "Blue", "Green", "Red", "Yellow"]
h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
我们也可以这样写:
items.size.times { |i| h[items[i]] = i }
#=> 5
h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
或
(0...items.size).each { |i| h[items[i]] = i }
#=> 0...5
h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
Ruby 的方式是跳过h = {} 这一步,像以前一样使用each_with_index,与Enumerator#with_object 一起使用:
items.each_with_index.with_object({}) { |(s,i),h| h[s] = i }
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
with_object 中的“对象”是一个散列,with_object 的参数是它的初始值,这里是一个空散列。该对象由块变量h 表示,在枚举items 的所有元素后返回(因此我们不需要后续行h 来返回哈希)。
让我们看看这里执行的步骤。首先,我们有
enum0 = items.each_with_index
#=> #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]:each_with_index>
我之前讨论过的。然后 Ruby 计算
enum1 = enum0.with_object({})
#=> #<Enumerator: #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]
:each_with_index>:with_object({})>
仔细检查返回值。如您所见,enum1 和enum0 一样,是一个枚举数。您可能会将其视为“复合枚举器”。要查看将传递给块的 enum1 的值,您可以将其转换为数组:
enum1.to_a
#=> [[["Aqua", 0], {}], [["Blue", 1], {}], [["Green", 2], {}],
# [["Red", 3], {}], [["Yellow", 4], {}]]
如您所见,enum1 有五个元素,每个元素都是一个包含数组和散列的数组。 enum1 的元素由Enumerator#each 传递给块,(它调用Array#each):
enum1.each { |(s,i),h| h[s] = i }
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
我们可以使用Enumerator#next 将enum1 的每个元素传递给块,并将块变量设置为其值。第一个是:
(s,i),h = enum1.next
#=> [["Aqua", 0], {}]
s #=> "Aqua"
i #=> 0
h #=> {}
注意[["Aqua", 0], {}] 是如何分解为三个组成元素的,并且每个块变量都设置为等于其中一个元素。这称为array decomposition。
我们现在可以进行块计算:
h[s] = i
#=> {}["Aqua"] = 0
现在:
h #=> {"Aqua"=>0}
然后将第二个元素传递给块:
(s,i),h = enum1.next
#=> [["Blue", 1], {"Aqua"=>0}]
s #=> "Blue"
i #=> 1
h #=> {"Aqua"=>0}
注意h 是如何更新的。块计算现在是:
h[s] = i
#=> {"Aqua"=>0}["Blue"] = 1
现在:
h #=> {"Aqua"=>0, "Blue"=>1}
其余的计算类似地执行。枚举完enum1 的所有元素后,enum1.each 返回h。