红宝石地图,地图!修改哈希数组

时间:2019-08-18 19:09:13

标签: ruby

所以mapmap!

foo = [1,2,3]
foo.map { |i| i*=2}
=> [2, 4, 6]
foo
=> [1, 2, 3] # foo unchanged

foo.map! { |i| i*=2}
=> [2, 4, 6]
foo
=> [2, 4, 6] # foo is changed

一切正常/预期。现在使用hash数组,让我们来做map

bar =  [{foo:1,bar:11}, {foo:2,bar:12}, {foo:3,bar:13}]
=> [{:foo=>1, :bar=>11}, {:foo=>2, :bar=>12}, {:foo=>3, :bar=>13}]
bar.map { |i| i[:foo]*= 2}
=> [2, 4, 6]
bar
=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}] # changed

因此,似乎使用map修改了哈希的基础数组,并且基本上与map!相同:

bar.map! do |i| 
 i[:foo]*=2 
 i 
end
=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]
bar
=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]

这里可能缺少一些基本知识。不寻找替代品,只是试图了解什么似乎是未记录的(?)注释/陷阱/不一致。 Tnx!

3 个答案:

答案 0 :(得分:2)

在第一个示例中,我们比较i的值:

foo = [1,2,3]
foo.map { |i| i*=2}

i这是一个数字。数字是不可变的。因此,在块中写入i * 2i *= 2没有区别。无论您使用map还是map!,分配都不会在任何地方进行。

即使您使用map!,为什么分配也不重要?我们可以快速重新实现map!来了解:

def my_map!(list, &blk)
  list.each_index { |i| list[i] = blk.call(list[i]) }
  list
end

如您所见,我们将每个索引的值设置为块的返回值。并且i * 2i *= 2返回值是相同的。

现在,看第二个例子:

bar =  [{foo:1,bar:11}, {foo:2,bar:12}, {foo:3,bar:13}]
bar.map { |i| i[:foo]*= 2}

为什么这会改变哈希值? i的值是一个散列,是可变的。分配键值(i[:foo]*= 2)会使它变异,无论它是否发生在eachmapmap!等中。

因此,为了追逐现实,您需要使用mergedup之类的东西来创建新的哈希:

bar =  [{foo:1,bar:11}, {foo:2,bar:12}, {foo:3,bar:13}]
bar.map { |i| i.merge(foo: i[:foo] * 2) }

答案 1 :(得分:2)

map方法不会更改数组的内容,这可以通过检查数组每个元素的object_id来看出。但是,每个元素都是一个可变的哈希,因此允许更新哈希的内容。从以下结果的逐步跟踪中可以看出:

p baz = [{foo:1,bar:11}, {foo:2,bar:12}, {foo:3,bar:13}]
puts "baz contents IDs are:"
baz.each { |hsh| p hsh.object_id }
puts "performing the map operation"
p baz.map { |i| i[:foo] *= 2 }
puts "baz contents IDs still are:"
baz.each { |hsh| p hsh.object_id }
puts "...but the contents of those contents have changed:"
p baz

产生例如:

[{:foo=>1, :bar=>11}, {:foo=>2, :bar=>12}, {:foo=>3, :bar=>13}]
baz contents IDs are:
70261047089900
70261047089860
70261047089840
performing the map operation:
[2, 4, 6]
baz contents IDs still are:
70261047089900
70261047089860
70261047089840
...but the contents of those contents have changed:
[{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]

请注意,我将数组的名称更改为baz,以避免由于名称阴影而引起的混乱。

答案 2 :(得分:1)

我想加两分钱。

当您在块中使用完全相同的代码(e[:foo] *= 2)时,您会看到mapmap!不同。


使用map

bar =  [ {foo:1, bar:11}, {foo:2, bar:12}, {foo:3, bar:13} ]
bar.map { |e| e[:foo] *= 2 } #=> [2, 4, 6]
bar #=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]

bar上,您获得与使用each完全相同的效果,除了调用的返回值:

bar =  [ {foo:1, bar:11}, {foo:2, bar:12}, {foo:3, bar:13} ]
bar.each { |e| e[:foo] *= 2 } #=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]
bar #=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]


使用map!

bar =  [ {foo:1, bar:11}, {foo:2, bar:12}, {foo:3, bar:13} ]
bar.map! { |e| e[:foo] *= 2 } #=> [2, 4, 6]
bar #=> [2, 4, 6]


Once @Amadam给了我一个非常有用的工具的链接,以了解代码执行过程中发生的事情,也许可以进一步帮助您:http://www.pythontutor.com/visualize.html#mode=edit