Ruby Map Method редактирует исходный массив? - PullRequest
2 голосов
/ 12 октября 2019
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"])

В этом коде исходный массив (arr) в методе map продолжает редактироваться. Я думал, что карта не редактирует исходный массив. Если бы мне нужно было отредактировать оригинал, я бы использовал .map!

Я считаю, что это как-то связано с вложенным перечислителем или ссылкой на переменную, которую я не вижу. Вместо each_char.with_index я использовал цикл while, и map все равно будет редактировать исходный массив. Почему исходный массив редактируется?

Ответы [ 4 ]

6 голосов
/ 12 октября 2019

Вы на самом деле не правы (по крайней мере) в двух местах:

  • map - это , а не редактирование исходного массива
  • фактически, оригиналмассив вообще не редактируется

Если присмотреться, массив не изменился, изменились только строки внутри массива . И это не map делает это, это String#[]=, который вы вызываете здесь:

c_word[index] = "*"

Итак, вы вызываете метод, который редактирует строки, и вы не должны удивлятьсячто ваши строки отредактированы!

5 голосов
/ 12 октября 2019

Подумайте об использовании:

  • map как "" Я хочу создать новые данные на основе существующих данных "
  • each как «Я либо не хочу изменять какие-либо данные, либо изменить существующие данные»

Имея это в виду, вы используете map с массивом для создания нового массивана основе существующего, а затем с помощью each изменить символы в существующих строках. Вот почему строки в исходном массиве в конечном итоге модифицируются.

Чтобы исправить это, дважды используйте map, сначала «создать новый массив на основе существующего массива», а затем второй раз «создать новую строку». на основе существующей строки ". Таким образом, исходные строки не будут изменены.

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__"]
1 голос
/ 12 октября 2019

Если вы не хотите изменять элементы массива, вы не должны их менять. Ваша проблема в этой строке:

c_word[index] = "*"

Так что просто используйте методы, которые не влияют на получателя, например:

def removal(array)
  letter = 'i'
  array.map { |word| word.gsub(letter, '*') }
end
1 голос
/ 12 октября 2019

Это происходит потому, что внутри блока карты вы выполняете некоторую обработку для каждого слова arr , а не для каждого слова new_array . Если вы хотите скопировать слова arr и изменить их на new_array , создайте копию, измените ее и верните слово.

Извлеките эти 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

...