从重复的数组元素中删除

时间:2011-03-26 18:17:17

标签: ruby arrays

从重复的数组元素中删除的最佳方法是什么。 例如,从数组

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

需要获得

a = [4, 1]

我的方法使用大量元素的速度太慢了。

arr = [4, 3, 3, 1, 6, 6]
puts arr.join(" ")
nouniq = []
l = arr.length
uniq = nil
for i in 0..(l-1)
  for j in 0..(l-1) 
    if (arr[j] == arr[i]) and ( i != j )
      nouniq << arr[j]
    end
  end
end
arr = (arr - nouniq).compact

puts arr.join(" ")

5 个答案:

答案 0 :(得分:4)

arr = [4, 3, 3, 1, 6, 6]

arr.
  group_by {|e| e }.
  map {|e, es| [e, es.length] }.
  reject {|e, count| count > 1 }.
  map(&:first)
# [4, 1]

答案 1 :(得分:4)

a = [4, 3, 3, 1, 6, 6]
a.select{|b| a.count(b) == 1}
#=> [4, 1]

更复杂但更快的解决方案(O(n)我相信:))

a = [4, 3, 3, 1, 6, 6]
ar = []
add = proc{|to, form| to << from[1] if form.uniq.size == from.size }
a.sort!.each_cons(3){|b| add.call(ar, b)}
ar << a[0] if a[0] != a[1]; ar << a[-1] if a[-1] != a[-2]

答案 2 :(得分:2)

不需要原始数组的单独副本并使用inject:

[4, 3, 3, 1, 6, 6].inject({}) {|s,v| s[v] ? s.merge({v=>s[v]+1}) : s.merge({v=>1})}.select {|k,v| k if v==1}.keys
 => [4, 1] 

答案 3 :(得分:1)

我需要这样的东西,所以测试了一些不同的方法。这些都返回原始数组中重复的项目数组:

module Enumerable
def dups
  inject({}) {|h,v| h[v]=h[v].to_i+1; h}.reject{|k,v| v==1}.keys
end
def only_duplicates
  duplicates = []
  self.each {|each| duplicates << each if self.count(each) > 1}
  duplicates.uniq
end
def dups_ej
  inject(Hash.new(0)) {|h,v| h[v] += 1; h}.reject{|k,v| v==1}.keys
end
def dedup
  duplicates = self.dup
  self.uniq.each { |v| duplicates[self.index(v)] = nil }
  duplicates.compact.uniq
end
end

Benchark结果为100,000次迭代,首先是一个整数数组,然后是一个字符串数组。性能将根据找到的重复项的数量而有所不同,但这些测试具有固定数量的重复项(〜半数组条目是重复的):

test_benchmark_integer
                                    user     system      total        real
Enumerable.dups                 2.560000   0.040000   2.600000 (  2.596083)
Enumerable.only_duplicates      6.840000   0.020000   6.860000 (  6.879830)
Enumerable.dups_ej              2.300000   0.030000   2.330000 (  2.329113)
Enumerable.dedup                1.700000   0.020000   1.720000 (  1.724220)
test_benchmark_strings
                                    user     system      total        real
Enumerable.dups                 4.650000   0.030000   4.680000 (  4.722301)
Enumerable.only_duplicates     47.060000   0.150000  47.210000 ( 47.478509)
Enumerable.dups_ej              4.060000   0.030000   4.090000 (  4.123402)
Enumerable.dedup                3.290000   0.040000   3.330000 (  3.334401)
..
Finished in 73.190988 seconds.

所以在这些方法中,似乎Enumerable.dedup算法是最好的:

  • 复制原始数组,因此它是不可变的
  • 获取uniq数组元素
  • 表示每个唯一元素:nil第一次出现在dup数组中
  • 压缩结果

如果只有(array - array.uniq)正常工作! (它不会 - 它会删除所有内容)

答案 4 :(得分:0)

这是我对Perl程序员使用哈希来累积数组中每个元素的计数的解决方案的转变:

ary = [4, 3, 3, 1, 6, 6]

ary.inject({}) { |h,a| 
  h[a] ||= 0
  h[a] += 1
  h 
}.select{ |k,v| v == 1 }.keys # => [4, 1]

如果这一点很重要,可以在一行上,明智地使用map中的行之间的分号。

一种不同的方式是:

ary.inject({}) { |h,a| h[a] ||= 0; h[a] += 1; h }.map{ |k,v| k if (v==1) }.compact # => [4, 1]

它将select{...}.keys替换为map{...}.compact,所以这并不是一个改进,对我来说有点难以理解。