All of the following apply to:
str = 'Codewars is the best site in the world'
Regarding the problem you are facing, let me break it down:
enum = str.enum_for(:scan,/[aeiou]/i)
To see the elements of this enumerator passed to map in its block, we can convert it to an array:
enum.to_a #=> ["o", "e", "a", "i", "e", "e", "i", "e", "i", "e", "o"]
Continuation:
arr = enum.map {Regexp.last_match.begin(0) } #=> [1, 3, 5, 9, 14, 17, 22, 24, 26, 31, 34] s_arr = arr.map{|x| x+1 } #=> [2, 4, 6, 10, 15, 18, 23, 25, 27, 32, 35] arr.each_with_index{|x,y| string[x] = s_arr[y].to_s} #=> [1, 3, 5, 9, 14, 17, 22, 24, 26, 31, 34] str #=> "C2d4w6rs 10s t15e18bes232527ite32i35 the world"
The method should return:
#=> "C2d4w6rs 10s th15 b18st s23t25 27n th32 w35rld"
So you see that the discrepancy starts with 'h' at 'the' , which is at index 13 . The problem occurs when the 10 of arr element is passed to the block. At this stage
str
For block variables, the value is set:
x,y = [10, 3] x
therefore block calculation:
str[10] = s_arr[3].to_s #=> = "10"
and now:
str
As you can see, the indices of all the letters following 10 increased by one. This was not a problem before, because the first three characters were replaced with a single digit. The remaining elements arr and s_arr now disabled by one, and after the next replacement, the indices of the remaining ones will be disabled by two, etc.
*********
I would use one of the following approaches.
VOWELS = 'aeiouAEIOU'
# one
pos = '0' str.gsub(/./) { |c| pos.next!; VOWELS.include?(c) ? pos : c }
# 2
str.each_char.with_index(1).map { |c,i| VOWELS.include?(c) ? i.to_s : c }.join
# 3
str.size.times.map { |i| VOWELS.include?(str[i]) ? (i+1).to_s : str[i] }.join
I have a slight preference for # 1, because it works with the string directly, unlike creating an array, and then attaches its elements back to the string. Moreover, I think he reads best.