-1

In Ruby (1.9.3) why does this code not change the array values:

arr = ['a', 'b', 'c']
arr.each do |e|
  e = 'new string'
end

While this code does change the hashes in item-b:

hashh = {
  'item-a' => 1,
  'item-b' => [
    {'id'=>'a','name'=>'name-a'},
    {'id'=>'b', 'name'=>'name-b'}
  ]
}

hashh['item-b'].each do |e|
  e['name'] = 'new-name'
end

I know that I can use map on the array to change the values by why does each allow me to change the hashes but not the array values?

Eric
  • 1,020
  • 1
  • 12
  • 18

2 Answers2

2

The first case has only local variable assignment:

arr.each do |e|
  e = 'new string'
end

Yet the second is actually using Hash#[]= which is a method. hash[:key] = "val" is syntactic sugar for hash.send(:[]=, :key, "val") in other words, and inside that method the actual updating of the hash happens via C code.

max pleaner
  • 26,189
  • 9
  • 66
  • 118
  • I kind of disagree with the "synactic sugar". In that sense, `foo.bar(true)` is syntactic sugar for `foo.send(:bar, true)`, which has nothing to do with the question. The point is that `[]=` is a method, whereas variable assignment is not. – Max Oct 23 '17 at 21:05
  • @Max fair point, I guess it'd be more accurate to say it's sugar for `hash.[]=(:foo, "bar")` (forgot that it was a public method) – max pleaner Oct 24 '17 at 00:54
2

In Ruby (1.9.3) why does this code not change the array values:

arr = ['a', 'b', 'c']
arr.each do |e|
  e = 'new string'
end

This code never does anything with the array values. Why would it change them? It simply assigns a string to the local variable e, which falls out of scope immediately, so the entire thing is a no-op.

While this code does change the hashes in item-b:

hashh = {
  'item-a' => 1,
  'item-b' => [
    {'id'=>'a','name'=>'name-a'},
    {'id'=>'b', 'name'=>'name-b'}
  ]
}

hashh['item-b'].each do |e|
  e['name'] = 'new-name'
end

Hash#[]= is a method which mutates the hash. Since you call a method which mutates the value (which is a hash), the value is mutated.

I know that I can use map on the array to change the values

map does not change the values. It returns a new array. (Well, obviously, you can change the values in a map, but that's not what it was designed for, and you shouldn't do it.)

by why does each allow me to change the hashes but not the array values?

each doesn't do anything here. In the second case, you call a method which mutates the value, ergo, the value gets mutated. In the first case, you don't call a method which mutates the value; in fact, you don't call any method at all. Ergo, the value doesn't get mutated.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653