Ruby Map方法编辑原始数组?

Ruby Map Method edits the original array?

def removal(arr)
    letters ="i"
    p arr  
    new_array = arr.map do |c_word|
        c_word.each_char.with_index do |char, index|
            if letters.include?(char)
                c_word[index] = "*"
            end    
        end
    end     
    p arr #the original array is getting edited? why?
    p new_array     

end

removal(["hiiiiiigh","git", "training"])

在此代码中,map 方法中的原始数组 (arr) 不断被编辑。我认为地图不会编辑原始数组。如果我需要编辑原件,那么我会使用 .map!

我相信它与我没有看到的嵌套枚举器或变量引用有关。我没有使用 each_char.with_index,而是使用了 while 循环,并且 map 仍会编辑原始数组。为什么要编辑原始数组?

发生这种情况是因为在地图块内,您正在对 arr 的每个词而不是 new_array[= 的每个词进行一些处理32=]。如果你想复制arr的话,然后在new_array中改变它然后创建一个副本,改变它并return这个词。

检查这 2 个代码,您就会明白我的意思

代码 1

def removal(arr)
    letters ="i"
    p arr
    new_array = arr.map do |c_word|
        c_word.each_char.with_index do |char, index|
            if letters.include?(char)
                c_word[index] = "*"
            end
        end
        c_word
    end
    p arr
    p new_array

end

removal(["hiiiiiigh","git", "training"])

这里你正在改变arr的单词并将其复制到new_array

代码 2

def removal(arr)
    letters ="i"
    p arr
    new_array = arr.map do |c_word|
        n_word = c_word.dup
        n_word.each_char.with_index do |char, index|
            if letters.include?(char)
                n_word[index] = "*"
            end
        end
        n_word
    end
    p arr
    p new_array

end

removal(["hiiiiiigh","git", "training"])

这里是复制arr的单词,修改后添加到new_array

考虑使用:

  • map 俗话说 "I want to create new data based on existing data"
  • each 俗话说 "I either want to not change any data, or change existing data"

考虑到这一点,您正在做的是使用 map 和数组来基于现有数组创建新数组,然后使用 each 修改现有字符串中的字符。这就是原始数组中的字符串最终被修改的原因。

要修复此问题,请使用 map 两次,第一次 "create new array based on existing array",然后第二次 "create new string based on existing string"。这样原始字符串就不会被修改。

def removal(arr)
  letters ="i"
  p arr
  new_array = arr.map do |word|
    word.chars.map do |char|
      letters.include?(char) ? '*' : char
    end.join
  end
  p arr
  p new_array
end

removal(["hiiiiiigh","git", "training"])  #=> ["hiiiiiigh", "git", "training"]
                                          #   ["hiiiiiigh", "git", "training"]
                                          #   ["h******gh", "g*t", "tra*n*ng"]

这个问题更实际的解决方案是这样的:

def censor(strings, forbidden_chars_string, censor_char = '*')
  re = Regexp.union(forbidden_chars_string.chars)
  strings.map {|str| str.gsub(re, censor_char) }
end

p ["hiiiiiigh","git", "training"]                     #=> ["hiiiiiigh", "git", "training"]
p censor(["hiiiiiigh","git", "training"], "i")        #=> ["h******gh", "g*t", "tra*n*ng"]
p censor(["hiiiiiigh","git", "training"], "gn", '_')  #=> ["hiiiiii_h", "_it", "trai_i__"]

如果您不想更改数组的元素,则不应更改它们。您的问题在这一行:

c_word[index] = "*"

所以只使用不影响收件人的方法,例如:

def removal(array)
  letter = 'i'
  array.map { |word| word.gsub(letter, '*') }
end

你实际上(至少)有两个地方是错误的:

  • map不是编辑原始数组
  • 事实上,根本没有编辑原始数组

如果仔细观察,数组没有变化,只有数组中的字符串发生了变化。这样做的不是 map,而是您在这里调用的 String#[]=

c_word[index] = "*"

所以,您调用的是一个编辑字符串的方法,您不应该对您的字符串被编辑感到惊讶!