2011-03-26 145 views
2

從數組元素中移除重複的最佳方法是什麼? 例如,從陣列從陣列元素中移除重複的元素

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(" ") 

回答

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] 
+0

這是有效的,但是請注意這是'O(n^2)',即對於大型數組非常低效(和OP的算法相同的順序) 。請參閱Jörg的回答以獲得有效答案 – 2011-03-26 19:31:54

+0

,但對於小型陣列,其速度會更快 – fl00r 2011-03-26 20:43:38

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] 
+0

+1爲自我記錄代碼:) – 2011-03-26 18:30:52

+0

@Joe:我仍在試圖找出更好的方法。有*必須*是一種使用'inject'的方式,因爲你可以簡單地證明你可以用'each'完成的所有事情都可以用'inject'完成,並且'Enumerable'中的所有方法都基於'each'。因此,任何可以用'Enumerable'方法組合的任何東西都可以用'inject'完成。這是否更具可讀性是另一回事。我想把它保持在O(n)的最後一步複雜度下,因爲@ fl00r已經提供了一個更好的O(n2)解決方案。 – 2011-03-26 18:40:15

+0

@Jörg你可以在Ruby中給出一些關於O(n)和O(n²)的鏈接:) – fl00r 2011-03-26 18:47:56

2

在不引入需要對原始陣列的單獨的副本,並使用注入:

[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] 
+0

[4,3,3,6,6] .inject({}){| s,v | s.merge(s [v]?{v => s [v] +1}:{v => 1})}。select {| k,v | k if v == 1} .keys – Wes 2011-03-26 23:36:52

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{...}.keysmap{...}.compact所以它不是一個真正的改善,而且,對我來說是有點難以明白。

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結果10萬次迭代,先用一個整數數組,然後一個字符串數組。性能會因重複的發現NUMER有所不同,但這些測試是用固定數量的重複(〜一半數組項是重複的):

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算法最好:

  • DUP原始數組所以它是不可變的
  • 獲取uniq的數組元素
  • 爲每個唯一的元素:無第一次出現的DUP陣列中
  • 緊湊的結果

如果只有(array - array.uniq)工作正常! (它不 - 它消除了一切)