在Ruby数组中,通过键删除重复项的最快/一行代码方法是什么?

7

如何以最快/最简单的方式基于特定键值或从方法返回的结果,移除对象数组中的重复项?

例如,我有20个XML元素节点,它们都具有相同的名称,但它们具有不同的“text”值,其中一些是重复的。我想通过说“如果element.text == previous_element.text,则将其删除”来移除重复项。在Ruby中,我该如何用最短的代码实现这个功能?

我已经知道了如何移除简单字符串/整数值的重复项,但不知道如何操作对象。


看看我的答案,以现代方式呈现。 - Marc-André Lafortune
3个回答

14

以下是标准的哈希方式。请注意使用 ||= 运算符,它是一种更方便的(a ||= b)方法,可以写成 a = b unless a

array.inject({}) do |hash,item|
   hash[item.text]||=item
   hash 
end.values.inspect

你也可以用一行代码实现。

这个脚本需要进行 O(n) 次字符串text的相等性检查。 当你看到哈希表时,这就是所覆盖的范围。


虽然它的时间复杂度为O(n^2),但并不是很重要,因为现在CPU时间非常便宜。 - EmFi
1
@EmFi ,访问哈希表不需要 O(n) 的时间复杂度(虽然我们需要遍历字符串“text”,但无论如何我们都必须这样做)。我刚刚在这个问题上发布了一个答案:https://dev59.com/03I-5IYBdhLWcg3w99kH#1590536 - P Shved
@Pavel 对不起,你是对的。 我一时糊涂,以为添加值调用会使其变成O(n ^ 2)。 实际上只会使其变成O(2n)。 - EmFi
1
这个答案是有效的,但已经过时了,请看我的答案。 - Marc-André Lafortune

10

这就是全部:

Hash[*a.map{|x| [x.text, x]}].values

短?没错。

(星号是可选的;似乎在1.8.6中是必需的)。

例如:

a = [Thing.new('a'), Thing.new('b'), Thing.new('c'), Thing.new('c')]
=> [#<Thing a>, #<Thing b>, #<Thing c>, #<Thing c>]

Hash[a.map{|x| [x.text, x]}].values
=> [#<Thing a>, #<Thing b>, #<Thing c>]

无聊的部分:这是我使用的小测试类:

class Thing
  attr_reader :text
  def initialize(text)
    @text = text
  end

  def inspect
    "#<Thing #{text}>"
  end
end

它在新版本中已经消失了,甚至更短、更简单 :). 然而,ary.map{|x| x.last}ary.map(&:last) 是等价的。 - Peter
我遇到了以下错误:在`[]'中:哈希表的参数数量为奇数(ArgumentError) - P Shved
你的输出是什么 a.map{|x| [x.text, x]}?我已经仔细检查过了,看起来没问题... - Peter
可以做到,但结果会少一项 :( 这是我的代码,这样你就可以全面了解:http://pastebin.com/m1594877c - P Shved
这个回答是有效的,但已经过时了,请看我的回答。 - Marc-André Lafortune
显示剩余2条评论

4

使用带有块的 Array#uniq。在您的情况下:

array.uniq(&:text) # => array with duplicated `text` removed

这个功能是在Ruby 1.9.2中引入的,所以如果使用早期版本,您可以使用backports,并使用require 'backports/1.9.2/array/uniq'


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