【问题标题】:Changing every value in a hash in Ruby在 Ruby 中更改哈希中的每个值
【发布时间】:2011-03-04 02:55:37
【问题描述】:

我想更改哈希中的每个值,以便在值之前和之后添加'%'

{ :a=>'a' , :b=>'b' }

必须改为

{ :a=>'%a%' , :b=>'%b%' }

最好的方法是什么?

【问题讨论】:

  • 请说明您是想改变原始字符串对象,还是改变原来的 has,或者什么都不改变。

标签: ruby hash


【解决方案1】:

在 Ruby 2.1 及更高版本中,您可以这样做

{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h

【讨论】:

  • 实际上,直到 Ruby v2.1 才可用
  • 不过,这非常慢并且非常耗内存。输入哈希被迭代以产生一组中间嵌套数组,然后将其转换为新哈希。忽略 RAM 峰值使用情况,运行时间要差得多 - 将其与另一个答案中的就地修改解决方案进行基准测试显示,在相同数量的迭代中,2.5 秒对 1.5 秒。由于 Ruby 是一种相对较慢的语言,因此避免使用慢速语言很有意义:-)
  • @AndrewHodgkinson 虽然总的来说我同意并且不提倡不关注运行时性能,但不跟踪所有这些性能缺陷开始成为一种痛苦并违背“开发人员生产力第一”红宝石哲学?我想这不是对你的评论,更多的是对使用 ruby​​ 给我们带来的最终悖论的一般评论。
  • 难题是:好吧,我们已经在决定使用 ruby​​ 时放弃了性能,那么“这一点”有什么不同呢?这是一个滑坡,不是吗?作为记录,从可读性的角度来看,我确实更喜欢这个解决方案而不是公认的答案。
  • 如果您使用 Ruby 2.4+,则使用 #transform_values! 会更容易,正如 sschmeck (stackoverflow.com/a/41508214/6451879) 所指出的那样。
【解决方案2】:

如果您希望实际的字符串本身在适当的位置发生变异(可能并且希望影响对相同字符串对象的其他引用):

# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |_,str| str.gsub! /^|$/, '%' }
my_hash.each{ |_,str| str.replace "%#{str}%" }

如果您希望哈希更改到位,但又不想影响字符串(您希望它获取新字符串):

# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |key,str| my_hash[key] = "%#{str}%" }
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }

如果你想要一个新的哈希:

# Ruby 1.8.6+
new_hash = Hash[*my_hash.map{|k,str| [k,"%#{str}%"] }.flatten]

# Ruby 1.8.7+
new_hash = Hash[my_hash.map{|k,str| [k,"%#{str}%"] } ]

【讨论】:

  • @Andrew Marshall 是的,谢谢。在 Ruby 1.8 中,Hash.[] 不接受数组对数组,它需要偶数个直接参数(因此前面有 splat)。
  • 其实Hash.[key_value_pairs]是1.8.7引入的,所以只有Ruby 1.8.6不需要splat & flatten。
  • @Aupajo Hash#each 生成块的键和值。在这种情况下,我不关心密钥,所以我没有给它命名任何有用的东西。变量名可以以下划线开头,实际上可能只是一个下划线。这样做并没有性能上的好处,它只是一个微妙的自我记录说明,我没有对第一个块值做任何事情。
  • 我想你的意思是my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h },必须从块中返回哈希
  • 另外,您可以使用 each_value 方法,这比使用下划线表示未使用的键值更容易理解。
【解决方案3】:

Ruby 2.4 引入了方法Hash#transform_values!,您可以使用它。

{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"} 

【讨论】:

  • 当然还有Hash#transform_values(不带刘海),它不会修改接收者。否则,一个很好的答案,谢谢!
  • 这真的会减少我对@9​​87654323@的使用:-p
【解决方案4】:

就地修改哈希值的最佳方法是

hash.update(hash){ |_,v| "%#{v}%" }

更少的代码和清晰的意图。也更快,因为除了必须更改的值之外没有分配新对象。

【讨论】:

  • 不完全正确:分配了新字符串。不过,一个有趣的解决方案是有效的。 +1
  • @Phrogz 好点;我更新了答案。通常无法避免值分配,因为并非所有值转换都可以表示为诸如gsub!之类的mutators。
  • 与我的答案相同,但有另一个同义词,我同意updatemerge! 更能传达意图。我认为这是最好的答案。
  • 如果不使用k,请改用_
【解决方案5】:

一个更易读的,map 它是一个单元素哈希数组,reducemerge

the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)

【讨论】:

  • 更清晰的使用Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
  • 这是一种效率极低的更新值的方法。对于每个值对,它首先创建一对Array(对于map)然后是Hash。然后,reduce 操作的每一步都会复制“memo”Hash 并将新的键值对添加到其中。至少在reduce 中使用:merge! 将最终的Hash 修改到位。最后,您不是在修改现有对象的值,而是创建了一个新对象,这不是问题所要求的。
  • 如果the_hash 为空,则返回nil
【解决方案6】:

这个任务有一个新的'Rails way'方法:) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values

【讨论】:

【解决方案7】:

一种不会给原始方法带来副作用的方法:

h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]

Hash#map 也可能是一个有趣的阅读,因为它解释了为什么 Hash.map 不返回哈希(这就是为什么 [key,value] 对的结果数组被转换为新的哈希)并提供了替代方法相同的一般模式。

编码愉快。

[免责声明:我不确定Hash.map 的语义是否在 Ruby 2.x 中发生变化]

【讨论】:

  • Matz 是否知道 Hash.map 语义在 Ruby 2.x 中是否发生变化?
  • Hash#[] 方法非常有用,但又丑陋。有没有更漂亮的方法以同样的方式将数组转换为哈希?
【解决方案8】:
my_hash.each do |key, value|
  my_hash[key] = "%#{value}%"
end

【讨论】:

  • 我不喜欢副作用,但该方法 +1 :) Ruby 1.9 (IIRC) 中有 each_with_object 可以避免直接访问名称,Map#merge 也可以工作。不确定复杂的细节有何不同。
  • 初始散列被修改——这没关系如果行为是预期的,但如果“忘记”可能会导致微妙的问题。我更喜欢减少对象的可变性,但它可能并不总是实用的。 (Ruby 几乎不是一种“无副作用”的语言 ;-)
【解决方案9】:

Hash.merge!是最干净的解决方案

o = { a: 'a', b: 'b' }
o.merge!(o) { |key, value| "%#{ value }%" }

puts o.inspect
> { :a => "%a%", :b => "%b%" }

【讨论】:

    【解决方案10】:

    像这样用 RSpec 测试后:

    describe Hash do
      describe :map_values do
        it 'should map the values' do
          expect({:a => 2, :b => 3}.map_values { |x| x ** 2 }).to eq({:a => 4, :b => 9})
        end
      end
    end
    

    您可以按如下方式实现 Hash#map_values:

    class Hash
      def map_values
        Hash[map { |k, v| [k, yield(v)] }]
      end
    end
    

    这个函数可以这样使用:

    {:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
    # {:a=>"%a%", :b=>"%b%"}
    

    【讨论】:

      【解决方案11】:

      如果您想知道哪个 inplace 变体是最快的,那就是:

      Calculating -------------------------------------
      inplace transform_values! 1.265k (± 0.7%) i/s -      6.426k in   5.080305s
            inplace update      1.300k (± 2.7%) i/s -      6.579k in   5.065925s
        inplace map reduce    281.367  (± 1.1%) i/s -      1.431k in   5.086477s
            inplace merge!      1.305k (± 0.4%) i/s -      6.630k in   5.080751s
              inplace each      1.073k (± 0.7%) i/s -      5.457k in   5.084044s
            inplace inject    697.178  (± 0.9%) i/s -      3.519k in   5.047857s
      

      【讨论】:

        猜你喜欢
        • 2021-12-07
        • 2015-09-15
        • 1970-01-01
        • 1970-01-01
        • 2015-08-26
        • 1970-01-01
        • 1970-01-01
        • 2014-04-09
        • 1970-01-01
        相关资源
        最近更新 更多