【问题标题】:Creating a hash automatically自动创建哈希
【发布时间】:2021-04-03 22:38:33
【问题描述】:

我有一个数组(array = ["home", "style", "honor", "home" ],我想做的是从这个数组创建一个哈希,如下所示:

hash = { "home" => 1, "style" => 2, "honor" => 3} #Array has two "home" element but I should give one value ("home" => 1) to both.

但我想知道有没有简单的方法可以做到这一点?比如 if i = 1 and "home" => 1 然后 i 将是 2,遵循 "style" => 2。我应该使用循环,enmerables 吗?还是有更好的办法?

【问题讨论】:

  • “我应该使用循环”——在 Ruby 中这个问题的答案总是“否”。几乎没有理由使用循环。
  • array = %w[home home style honor style honor home floor] 的结果是什么?
  • hash = {home => 1, style => 2, Honor => 3, home =>4, floor => 5}
  • 这是不可能的。键必须是唯一的。
  • 我不明白你的问题。你的期望的输出是不可能的。你尝试用什么来生产它并不重要。它不能被生产出来。

标签: arrays ruby ruby-hash


【解决方案1】:

我们需要做的第一件事是从Array 中删除重复的元素。为此,我们可以使用Enumerable#uniq 方法:

array.uniq
#=> ['home', 'style', 'honor']

接下来,我们需要将每个元素与其索引配对。为此,我们可以使用Enumerable#each_with_index 方法,它为我们提供了一个Enumerator,即yields 原始元素及其索引的二元素数组:

array.uniq.each_with_index
#=> #<Enumerator: ['home', 'style', 'honor']:each_with_index>

我们可以通过将Enumerator 转换为Array 来看看会发生什么:

array.uniq.each_with_index.to_a
#=> [['home', 0], ['style', 1], ['honor', 2]]

索引是一对一的,但我们可以使用Enumerable#map 解决这个问题:

array.uniq.each_with_index.map {|el, i| [el, i.succ] }
#=> [['home', 1], ['style', 2], ['honor', 3]]

或者,我们可以在使用Hash#transform_values 方法创建Hash 之后修复它。

另一种可能性是首先使用Array#eachArray 转换为Enumerator,然后使用带有可选偏移参数的Enumerator#with_index 方法:

array.uniq.each.with_index(1)
#=> #<Enumerator: #<Enumerator: ['home', 'style', 'honor']:each>:with_index(1)>

array.uniq.each.with_index(1).to_a
#=> [['home', 1], ['style', 2], ['honor', 3]]

为了创建Hash,我们需要做的就是使用Enumerable#to_h 方法将我们已经拥有的二元素“对”转换为键值对。 Enumerable#to_h 还接受一个可选块,这是我们转换索引的另一种可能方式。

array.uniq.each.with_index(1).to_h
array.uniq.each_with_index.to_h {|el, i| [el, i.succ]}
array.uniq.each_with_index.to_h.transform_values(&:succ)
#=> { 'home' => 1, 'style' => 2, 'honor' => 3 }

【讨论】:

    【解决方案2】:

    这是一种单一的方法:

    array
      .uniq
      .each_with_object({})
      .each_with_index { |(item, hash), index| 
          hash[item] = index + 1 
        }
    

    基本上,我首先删除重复项,然后使用哈希和手头的索引迭代数组(现在基本上是一个集合),然后将下一个索引分配给每个项目。我在每个结果中加了 1,只是为了匹配您想要的结果。

    【讨论】:

      【解决方案3】:

      可以一次获得所需的哈希,从而避免创建临时数组。

      array = ["home", "style", "honor", "home", "style", "hello"] 
      
      e = 1.step
      array.each_with_object({}) { |x,h| h[x] = e.next unless h.key?(x) }
        #=> {"home"=>1, "style"=>2, "honor"=>3, "hello"=>4}
      

      查看返回枚举数的Numeric#step 形式。密钥查找 (h.key?(x)) 非常快,所需时间几乎与哈希大小无关。

      【讨论】:

      • 似乎h[x] ||= e.next 将同样有效,因为h 没有default_valuee.next 永远不会是nilHash#key?Hash#[] 都使用相同的功能 (hash_stlike_lookup) 或者我们可以使用默认的 array.each_with_object(Hash.new {|h,k| h[k] = e.next}) {|x,h| h[x] }
      • @engineersmnky,所有优点。我特别喜欢你的{ |x,h| h[x] },因为它非常神秘。为了可读性,我更喜欢h.key?(x)
      • 如果您正在寻找神秘的事物,那么 {_2[_1]} (ruby >= 2.7.0 Numbered block parameters) 或者更好的是 array.each.with_index(1).with_object({}) {_2.store *_1 unless _2.key?_1[0] }
      • @engineersmnky,看来你正在享受你的圣诞蛋酒。注意肯定更好; _2 不是随着每个_1 递增吗?
      • 你是对的。它需要一个uniq,尽管_1[1]会随着每个_1[0]而增加(_2将是注入的Hash
      猜你喜欢
      • 2013-12-20
      • 2010-10-22
      • 2013-03-05
      • 2011-09-20
      • 2021-02-21
      • 2012-06-29
      • 2015-10-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多