如何有效地提取Ruby数组中的重复元素?

时间:2009-04-24 17:39:05

标签: ruby arrays count elements

我有一个类似[1,1,1,2,4,6,3,3]的数组,我想得到重复元素的列表,在本例中为[1,3]。我写了这个:

my_array.select{|obj|my_array.count(obj)>1}.uniq

但是它的效率很低(o(n²))。你有更好的主意吗?如果可能的话简洁。

由于

8 个答案:

答案 0 :(得分:9)

受伊利亚·海金森的回答启发:

def repeated(array)
  counts = Hash.new(0)
  array.each{|val|counts[val]+=1}
  counts.reject{|val,count|count==1}.keys
end

答案 1 :(得分:6)

使用Ruby的Set库:

require 'set'

ary = [1,1,1,2,4,6,3,3]
dups = Set.new
test_set = Set.new
ary.each {|val| dups.add(val) unless test_set.add?(val)}
dups.to_a # [1, 3]

我认为这应该是O(n),因为Set#add和Set#add?据我所知,是恒定时间操作。

答案 2 :(得分:4)

这样的事情怎么样?它将在O(n)中运行。

a = [1,1,1,2,4,6,3,3]
b = {}
a.each { |v| if b.has_key? v then b[v] = b[v]+1 else b[v]=1 end }
b.reject { |k,v| if v > 1 then false else true end }.keys

答案 3 :(得分:3)

O(n)解决方案(将<< x更改为+ [x]并将update更改为merge以使其完全正常运行):

rs = xs.inject([[], {}]) do |(out, seen), x| 
  [(seen[x] == 1 ? (out << x) : out), seen.update(x => (seen[x] || 0)+1)]
end[0]

一种更简单但更节省空间的方法:

rs = xs.group_by { |x| x }.select { |y, ys| ys.size > 1 }.keys

使用“list-comprehension”避免中间哈希的相同想法:

rs = xs.group_by { |x| x }.map { |y, ys| y if ys.size > 1 }.compact

答案 4 :(得分:1)

使用inject

[1,1,1,2,4,6,3,3].inject({}){ |ele, n| ele[n] = nil; ele }.keys 
# => [1, 2, 4, 6, 3] 

说明:

ele哈希它的初始化为{},每次迭代时,一个带有nnil值的键被添加到ele哈希。最后ele返回为:

{1=>nil, 2=>nil, 4=>nil, 6=>nil, 3=>nil}

我们只需要密钥,因此.keys结束工作。

答案 5 :(得分:0)

一些想法:你必须弄清楚正确的库数据结构:

  

1 对数组O(nlogn)进行排序,然后运行数组

     

2 创建一个集合,搜索集合中的当前数组元素,如果未找到,则插入并继续处理所有元素 - O(nlogn)。

答案 6 :(得分:0)

我在考虑计算一个独特元素在数组中出现的次数。 就像最初的建议一样,这可能是非常低效的,但看着这个问题很有趣。 我没有在较大的阵列上做任何基准测试,所以这只是一个练习。

a = [1,1,1,2,4,6,3,3]

dupes = []
a.uniq.each do |u|
  c = a.find_all {|e| e == u}.size
  dupes << [u, c] unless c == 1
end

puts dupes.inspect

# dupes = [[1, 3], [3, 2]]
# 1 appears 3 times
# 3 appears twice


# to extract just the elment a bit cleaner
dupes = a.uniq.select do |u|
  a.find_all {|e| e == u}.size != 1
end
puts dupes.inspect
# returns [1,3]

答案 7 :(得分:0)

如果重复的条目总是连续的,这将起作用,如您的示例所示;否则你必须先排序。 each_cons检查指定大小的滚动窗口。

require 'set'

my_array = [1,1,1,2,4,6,3,3]
dups = Set.new
my_array.each_cons(2) {|a,b| dups.add(a) if (a == b)}
p dups.to_a