Удалить элементы массива, присутствующие в другом массиве

есть список слов и список запрещенных слов. Я хочу просмотреть список слов и отредактировать все запрещенные слова. Это то, что я закончил делать (обратите внимание на catched boolean):

puts "Give input text:"
text = gets.chomp
puts "Give redacted word:"
redacted = gets.chomp

words = text.split(" ")
redacted = redacted.split(" ")
catched = false

words.each do |word|
  redacted.each do |redacted_word|
    if word == redacted_word
        catched = true
        print "REDACTED "
        break
    end
  end
    if catched == true
        catched = false
    else
        print word + " "
    end
end

есть ли какой-либо правильный/эффективный способ?

3 ответов


можно использовать .reject чтобы исключить все запрещенные слова, которые присутствуют в redacted время:

words.reject {|w| redacted.include? w}

демо

если вы хотите получить список запрещенных слов, которые присутствуют в words массив, вы можете использовать .select:

words.select {|w| redacted.include? w}

демо


Он также может работать.

words - redacted

+, -, &, эти методы очень просты и полезны.

irb(main):016:0> words = ["a", "b", "a", "c"]
=> ["a", "b", "a", "c"]
irb(main):017:0> redacted = ["a", "b"]
=> ["a", "b"]
irb(main):018:0> words - redacted
=> ["c"]
irb(main):019:0> words + redacted
=> ["a", "b", "a", "c", "a", "b"]
irb(main):020:0> words & redacted
=> ["a", "b"]

это может быть немного более "элегантный". Будет ли это более или менее эффективным, чем ваше решение, я не знаю.

puts "Give input text:"
original_text = gets.chomp
puts "Give redacted word:"
redacted = gets.chomp

redacted_words = redacted.split

print(
  redacted_words.inject(original_text) do |text, redacted_word|
    text.gsub(/\b#{redacted_word}\b/, 'REDACTED')
  end
)

так, что здесь происходит?

  • я использую String#split без аргументов, потому что ' ' по умолчанию, все равно.
  • С Array#inject, следующий блок (глядя на do и заканчивая end выполняется для каждого элемента массива-в этом случае наш список запрещенных слова.
    • в каждом раунде вторым аргументом блока будет соответствующий элемент из массива
    • первым аргументом блока будет возвращаемое значение блока из предыдущего раунда. Для первого раунда аргумент функции inject (в нашем случае original_text) будет использоваться.
    • возвращаемое значение блока из последнего раунда будет использоваться в качестве возвращаемого значения функции inject.
  • в блоке, я замените все вхождения текущего обработанного отредактированного слова в тексте.
    • String#gsub выполняет gлобальные substitution
    • в качестве шаблона для замены я использую литерал regexp (/.../). Кроме того, это не совсем литерал, поскольку я выполняю подстановку строки (#{...}) на нем, чтобы получить в настоящее время обработанное отредактированное слово.
    • в регулярном выражении я окружаю слово, которое нужно отредактировать с \b слово граничный matchers. Они соответствуют границе между буквенно-цифровыми и не буквенно-цифровыми символами (или vice verca), не совпадая ни с одним из символов. (Они соответствуют нулевой "позиции" между символами.) Если строка начинается или заканчивается с символов \b также будет соответствовать началу или концу строки, соответственно, так что мы можем использовать его для сопоставления целых слов.
  • результат inject (что является результатом последнего выполнения блока, т. е. текста, когда все замены имели место) передается в качестве аргумента print, который выведет теперь отредактированный текст.

Примечание что, кроме вашего решения, мое не будет рассматривать пунктуацию как части соседних слов.

и Примечание что мое решение будет уязвимо для инъекции regex.

пример 1:

Give input text:
A fnord is a fnord.
Give redacted word:
ford fnord foo

мой вывод:

A REDACTED is a REDACTED.

выход:

A REDACTED is a fnord.

Пример 2:

Give input text:
A fnord is a fnord.
Give redacted word:
fnord.

мой вывод:

A REDACTEDis a fnord.

(обратите внимание, как . было истолковано как любой символ.)

выход:

A fnord is a REDACTED.