在Ruby中更改哈希中的每个值


170

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

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

必须更改为

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

最好的方法是什么?


1
请说明您是否要更改原始字符串对象,仅更改原始字符串对象还是不进行任何更改。
Phrogz'3

1
然后,您接受了错误的答案。(@pst的意图不是冒犯,因为我个人也主张使用函数式编程而不是
更改

但是方法还是不错的
TheReverseFlick 2011年

Answers:


178

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

# 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}%"] } ]

1
@安德鲁·马歇尔对了,谢谢。在Ruby 1.8中,Hash.[]它不接受数组对的数组,它需要偶数个直接参数(因此前者为splat)。
Phrogz'3

2
实际上,Hash。[key_value_pairs]是在1.8.7中引入的,因此仅Ruby 1.8.6不需要splat和flatten。
马克-安德烈·Lafortune

2
@Aupajo Hash#each将密钥和值同时产生。在这种情况下,我不在乎密钥,因此我没有给它命名任何有用的东西。变量名可能以下划线开头,实际上可能只是下划线。这样做没有性能上的好处,这只是一个微妙的自我说明,我没有使用第一个块值做任何事情。
Phrogz 2011年

1
我想你的意思是my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h },必须从块返回哈希
aceofspades

1
或者,您可以使用each_value方法,该方法比未使用的键值使用下划线要容易理解。
Strand McCutchen

266

在Ruby 2.1及更高版本中,您可以执行

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

5
谢谢,实际上是我想要的。不知道为什么您没有得到足够的支持。
simperreault

7
但是,这非常慢并且非常消耗RAM。对输入哈希进行迭代以生成中间的嵌套数组集,然后将其转换为新的哈希。忽略RAM峰值使用率,运行时间要差得多-在另一个答案中将此基准与就地修改解决方案进行基准测试表明,在相同的迭代次数下为2.5s与1.5s。由于Ruby是一种相对较慢的语言,因此避免使用慢速语言很有意义:-)
Andrew Hodgkinson

2
@AndrewHodgkinson虽然总体上我同意并且不主张不关注运行时性能,但是不跟踪所有这些性能陷阱会变得很痛苦并且与“开发人员生产力优先”的红宝石哲学背道而驰吗?我想这对您来说不是什么评论,而更多地是关于最终使用Ruby带来的悖论的一般评论。
elsurudo

4
难题是:好吧,我们在使用甚至红宝石的决定中已经放弃了性能,那么“这一点”有什么不同?这是一个湿滑的斜坡,不是吗?从记录的角度来看,从可读性的角度来看,我确实更喜欢这种解决方案而不是公认的答案。
elsurudo

1
如果您使用Ruby 2.4+,则#transform_values!如sschmeck(stackoverflow.com/a/41508214/6451879)所指出的那样,它甚至更容易使用。
Finn


86

修改哈希值的最佳方法是

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

更少的代码和明确的意图。由于没有分配超出必须更改的值的新对象,因此速度也更快。


并非完全正确:分配了新字符串。仍然是一个有效的有趣解决方案。+1
Phrogz 2014年

@Phrogz好点; 我更新了答案。通常无法避免值分配,因为并非所有值转换都可以表示为mutator,例如gsub!
2014年

3
与我的回答相同,但有另一个同义词,我同意update传达的意图要比更好merge!。我认为这是最好的答案。

1
如果不使用k,请_改用。
sekrett

28

一点更可读的一个,map它为单元素散列的阵列,并且reduce,与merge

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

2
使用更清晰Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
meagar

这是一种非常低效的更新值的方法。对于每个值对,它首先创建一个Array(对map),然后创建一个Hash。然后,reduce操作的每个步骤都将复制“备忘录”,Hash并向其添加新的键值对。至少使用:merge!in reduce修改最终Hash的位置。最后,您不是在修改现有对象的值,而是创建一个新的对象,这不是问题要问的。
Sim

它返回nil是否the_hash为空
DNNX


16

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

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

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

快乐的编码。

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


7
Matz甚至不知道Hash.mapRuby 2.x中的语义是否发生了变化?
Andrew Grimm'3

1
Hash#[]方法非常有用,但是却很丑陋。是否有更漂亮的方法将数组转换为哈希值?
马丁·

15
my_hash.each do |key, value|
  my_hash[key] = "%#{value}%"
end

我不喜欢副作用,但是方法+1 :) each_with_objectRuby 1.9(IIRC)中提供了这种方法,可以避免直接访问名称,Map#merge也可以使用。不确定复杂的细节有何不同。

1
修改了初始哈希- 如果可以预料到这种行为就可以,但是如果“忘记”了,可能会引起细微的问题。我更喜欢减少对象的可变性,但这可能并不总是实用的。(Ruby几乎不是“无副作用”的语言;-)

8

哈希合并!是最干净的解决方案

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

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

@MichieldeMare对不起,我没有对此进行足够彻底的测试。没错,该块需要使用两个参数。已更正。

5

在像这样用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%"}

1

如果您想知道哪种就地变体最快,请按以下步骤操作:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.