【问题标题】:Converting Ruby array into a hash将 Ruby 数组转换为哈希
【发布时间】:2016-02-12 08:06:39
【问题描述】:

我正在尝试编写一个名为 my_transform 的方法,该方法采用如下数组:

items = ["Aqua", "Blue", "Green", "Red", "Yellow"]

并显示项目的索引如下:

item_to_position = {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}

我应该能够执行:

my_transform(items) == item_to_position

并接收true

我曾考虑使用each_with_index。我应该先说:

items = ["Aqua", "Blue", "Green", "Red", "Yellow"]

hash = Hash[*array]

def my_transform

我必须将字符串转换为哈希。任何帮助表示赞赏。

【问题讨论】:

  • 恐怕以“我已经考虑过”开头的部分对我来说没有什么意义。 my_transform 的正文不见了……它是否包含我们应该看到的代码?此外,“我必须将字符串转换为哈希”是问题中第一次提到任何字符串操作。什么字符串?

标签: arrays ruby hash


【解决方案1】:

我会使用Array#to_h:

items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
items.each_with_index.to_h
#=> { "Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4 }

请注意,to_h 是在 Ruby 2.1 中引入的

使用to_h 您的my_transform 方法可能如下所示:

def my_transform(items)
  items.each_with_index.to_h
end

【讨论】:

【解决方案2】:

您可以通过多种方式做到这一点。

创建一个数组并将其转换为哈希

直到最近,您还可以使用公共类方法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_hEnumerable#to_h 方法。第一个是这样的:

h = [[:a, 1], [:b, 2]].to_h
  #=> {:a=>1, :b=>2}

因此,要使用Hashto_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_aitems 的每个元素转换为数组[<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"=&gt;0,然后用"Aqua"=&gt;3 覆盖,然后用"Aqua"=&gt;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({})> 

仔细检查返回值。如您所见,enum1enum0 一样,是一个枚举数。您可能会将其视为“复合枚举器”。要查看将传递给块的 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#nextenum1 的每个元素传递给块,并将块变量设置为其值。第一个是:

(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

【讨论】:

  • 太好了,卡里。非常感谢您花时间全面解释数组、哈希和枚举。清晰多了!
【解决方案3】:
def my_transform(arr)
  arr.inject({}) {|m,e| m[e] = arr.index(e); m }
end

【讨论】:

  • @Stefan 好吧,那不是因为哈哈键是唯一的吗?
  • @MichalSzyndel 哦,当然! :-)(好吧,除非设置了compare_by_identity
  • 你可以通过使用Enumerable#each_with_object来摆脱那个讨厌的; marr.each_with_object({}) {|e,m| m[e] = arr.index(e) }
【解决方案4】:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
def my_transform(items)     
   Hash[items.each_with_index.map { |value, index| [value, index] }]
end

【讨论】:

  • Hash[items.each_with_index.to_a] 应该足够了。
  • @ArieShaw:哦,是的,那就更好了!感谢您指出:)!
【解决方案5】:

大多数红宝石

这至少可以追溯到 Ruby 1.9.3。

# Verbose, but flexible!
def hasherize *array
  hash = {}
  array.flatten!
  array.each_with_index { |key, value| hash[key] = value }
  hash
end

# Pass a single array as an argument.
hasherize %w(Aqua Blue Green Red Yellow)
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}

# Pass multiple arguments to the method.
hasherize :foo, :bar, :baz
#=> {:foo=>0, :bar=>1, :baz=>2}

红宝石 >= 2.1.0

如果您正在运行最近的 Ruby,您可以将上述内容简化为:

def hasherize *array
  array.flatten.each_with_index.to_h
end

结果将与上面相同,但Array#to_h 方法大大简化了代码。但是,您仍然需要展平数组以避免出现以下结果:

#=> {["Aqua", "Blue", "Green", "Red", "Yellow"]=>0}

【讨论】:

  • 你为什么打电话给flatten
  • Hash[array.each_with_index.to_a] 将适用于 Ruby 1.9.3
【解决方案6】:

你也可以试试这个。

例如

items = ["Aqua", "Blue", "Green", "Red", "Yellow"]

items.inject({}) do |tmphash, (k,v)|
  tmphash[k] = items.index(k)
  tmphash
end

## OUTPUT

{"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}

【讨论】:

  • 你为什么打电话给group_by(&amp;:itself)?构建{"Aqua"=&gt;["Aqua"], "Blue"=&gt;["Blue"], ...} 哈希有什么好处?
  • 我用它对重复值进行分组。
  • 我会使用 uniq 从数组中删除重复项,但我什至不确定这是一个要求。
猜你喜欢
  • 1970-01-01
  • 2021-01-17
  • 1970-01-01
  • 2010-12-11
  • 2021-10-16
  • 1970-01-01
  • 2021-11-25
  • 1970-01-01
  • 2017-07-06
相关资源
最近更新 更多