使用 Ruby 删除数组中的特定重复项

4

有没有一种方法可以使用Ruby从数组中删除特定的重复项?示例数组:

["hello", "removeme", "removeme", "hello", "testing"]

我只想在该数组中删除"removeme"的重复项。期望的输出是:

["hello", "removeme", "hello", "testing"]

有没有类似这样的方法可以得到期望的输出?["hello", "removeme", "removeme", "hello", "testing"].uniq('removeme')


1
如果数组是["hello", "removeme", "hello", "testing", "removeme"],那么期望的结果是什么?我想知道保留元素的选择(这里是第一个或第二个实例的“removeme”)是否重要。 - Cary Swoveland
嗨@CarySwoveland,期望的结果将是["hello", "removeme", "hello", "testing"],我想要删除具有特定字符串的重复项,例如上面的例子中,我想要在数组中保留一个removeme值。我不能使用uniq,因为它会影响数组中的hello值。 - oj5th
2
你说,“我想在数组中保留一个removeme值”,但我问你要保留哪一个。说“期望的结果将是["hello", "removeme", "hello", "testing"]”表明您希望保留第一个实例,但可能您不关心保留哪一个。您需要澄清这一点。在澄清问题时,最好编辑您的问题而不是在评论中详细说明,因为问题应该是独立的--读者不应该被要求阅读所有评论才能理解问题。 - Cary Swoveland
2
这是一个纯 Ruby 的问题,所以你不应该使用 Rails 标签。 - Cary Swoveland
4个回答

8

您可以使用 uniq 方法来去除基于代码块返回值的重复值:

ary = ["hello", "removeme", "removeme", "hello", "testing"]

ary.uniq { |obj| obj.eql?('removeme') || Object.new }
#=> ["hello", "removeme", "hello", "testing"]

对于等于 'removeme' 的元素,我们返回 true,而对于其他所有元素('hello''hello''testing'),我们返回一个新对象:(请注意不同的对象 ID)
"hello"#<Object:0x00007f9ab08590d8>
"removeme" → true
"removeme" → true
"hello"#<Object:0x00007f9ab0858d68> 
"testing"#<Object:0x00007f9ab08589f8>

具有相同返回值的所有元素都被视为重复项,即使其实际值不同,uniq 也会将 'removeme' 视为重复项,而将其他任何内容视为唯一项。这允许两个相同的 'hello' 字符串保持不变。

您还可以使用元素的索引,而不是 Object.new

ary.enum_for(:uniq).with_index { |obj, i| obj.eql?('removeme') || i }
#=> ["hello", "removeme", "hello", "testing"]

enum_for是必需的,因为没有块的uniq返回一个新的数组,而不是一个枚举器(这在链式调用with_index时需要)。


2
聪明!我也尝试过类似的事情,但在最后一步失误了。我想另外一种方法是 n = 0; ary.uniq { |obj| obj.eql?('removeme') || n += 1 } - Cary Swoveland
1
@CarySwoveland,我已经加入了您使用元素索引的方法。 - Stefan

4

如果只保留第一次出现的单词,可以这样做。

def uniq_word(array, word)
  return array unless first = array.index(word)
  array.reject.with_index { |elem, index| index > first && elem == word }
end

array = ["hello", "removeme", "removeme", "hello", "testing"]

uniq_word(array, 'removeme')
#=> ["hello", "removeme", "hello", "testing"]

查看 Array#index, Enumerator#with_indexArray#reject
或者你可以遍历数组,并将第一次出现的元素复制到一个新的数组中:
def uniq_word(array, word)
  found = false

  [].tap do |result|
    array.each do |elem|
      if elem == word && !found
        found = true
        next
      end

      result << elem
    end
  end
end

array = ["hello", "removeme", "removeme", "hello", "testing"]

uniq_word(array, 'removeme')
#=> ["hello", "removeme", "hello", "testing"]

请参见:


3

可以使用以下方法。

def removem(arr, to_remove)
  i = arr.index(to_remove)
  i.nil? ? arr : (arr - [to_remove]).insert(i, to_remove)
end

假设给定的数组如下。
arr = ["hello", "removeme", "hello", "goodbye", "removeme", "testing"]

那么,

removem(arr, "removeme")
  #=> ["hello", "hello", "goodbye", "testing", "removeme"]
removem(arr, "goodbye")
  #=> ["hello", "removeme", "hello", "goodbye", "removeme", "testing"]
removem(arr, "missing")
  #=> ["hello", "removeme", "hello", "goodbye", "removeme", "testing"]

在每个示例中,arr 都不会改变。
请参见 Array#indexArray#insert

-2

通过添加新方法扩展数组类怎么样?

class Array
  def uniq_specific!(targets)
    found = {}
    targets.each { |target| found[target] = false }

    delete_if do |item|
      if targets.include?(item)
        if found[item]
          true
        else
          found[item] = true
          false
        end
      else
        false
      end
    end
  end
end

array = ["hello", "removeme", "removeme", "hello", "world", "world", "testing"]
array.uniq_specific!(["removeme", "hello"])

array # => ["removeme", "hello", "world", "world", "testing"]

我们为数组类定义了一个新的方法uniq_specific!,它接受一个目标元素数组作为参数。它遍历数组并删除每个目标元素的所有出现次数,除了第一次出现的。然后,我们使用目标值["removeme", "hello"]在示例数组上调用此方法,它会产生所需的输出。

我给你点踩是因为首次出现的"hello""removeme"都被移除了,而不仅仅是后者。即使方法是正确的,也没有理由通过添加新方法来污染Array类。 - Cary Swoveland
正如您所知,这个问题中没有这样的条件,我提出了另一种实现选项。 - Andrew Zhuk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接