Enumerable#map
creates an array with the return values in the block after it's yielded.
In such case, say:
v = 'a'
26.times.map { |i| v.ord.+(i).chr }
# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
But why does the following codes fills the array with same elements?
v = '`'
26.times.map { v.next! }
# => ["z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z"]
v = '`'
Array.new(26) { v.next! }
# => ["z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z"]
Shouldn't they all have elements a to z?
Again, this works:
v = '`'
Array.new(26) { v = v.succ }
# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
Actually I am trying to do:
v = "\xf0\x9d\x93\xa9"
('a'..'z').reduce({}) { |h, i| h.merge(i.intern => v = v.succ) }
# => {:a=>"𝓪", :b=>"𝓫", :c=>"𝓬", :d=>"𝓭", :e=>"𝓮", :f=>"𝓯", :g=>"𝓰", :h=>"𝓱", :i=>"𝓲", :j=>"𝓳", :k=>"𝓴", :l=>"𝓵", :m=>"𝓶", :n=>"𝓷", :o=>"𝓸", :p=>"𝓹", :q=>"𝓺", :r=>"𝓻", :s=>"𝓼", :t=>"𝓽", :u=>"𝓾", :v=>"𝓿", :w=>"𝔀", :x=>"𝔁", :y=>"𝔂", :z=>"𝔃"}
But I get all z's instead while using succ!
/ next!
v = "\xf0\x9d\x93\xa9"
('a'..'z').reduce({}) { |h, i| h.merge(i.intern => v.succ!) }
# => {:a=>"𝔃", :b=>"𝔃", :c=>"𝔃", :d=>"𝔃", :e=>"𝔃", :f=>"𝔃", :g=>"𝔃", :h=>"𝔃", :i=>"𝔃", :j=>"𝔃", :k=>"𝔃", :l=>"𝔃", :m=>"𝔃", :n=>"𝔃", :o=>"𝔃", :p=>"𝔃", :q=>"𝔃", :r=>"𝔃", :s=>"𝔃", :t=>"𝔃", :u=>"𝔃", :v=>"𝔃", :w=>"𝔃", :x=>"𝔃", :y=>"𝔃", :z=>"𝔃"}
Except succ!
/ next!
doesn't change the memory location and object_id, aren't v.succ!
and v = v.succ
same?
When you call next!
or succ!
on a variable str
, object assigned to this variable is mutated and a reference to this object is returned. If str = 'a'
and you call str.next!
26 times, str becomes z
. Every time next!
is called, a reference to the same object is returned. As a result, you get an array of 26 references to the same object. That's why all of the elements in the array are the same.
You can test that by checking object_id
of array elements:
pry(main)> str = 'a'
'a'
pry(main)> array = 3.times.map{ str.next!}
=> ["d", "d", "d"]
pry(main)> array.map(&:object_id)
=> [47056742362940, 47056742362940, 47056742362940]
pry(main)> array.map(&:object_id).uniq
=> [47056742362940]
When you edit str
, all array elements are updated:
[39] pry(main)> str << "b"
=> "db"
[40] pry(main)> array
=> ["db", "db", "db"]
[41] pry(main)> str.replace
str.replace
[41] pry(main)> str.replace('a')
=> "a"
[42] pry(main)> array
=> ["a", "a", "a"]
If you want to have an array with the whole alphabet, you need to copy the string after changing current letter, see below:
[25] pry(main)> str = 'a'
=> "a"
[26] pry(main)> 25.times.map{ str.next!.dup}
=> ["b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"w",
"x",
"y",
"z"]
You can also use a range:
[32] pry(main)> ('a'..'z').to_a
=> ["a",
"b",
"c",
"d",
"e",
"f",
...