2011-03-26 4 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

이 작업은 효과적이지만 큰 배열 (OP의 알고리즘과 동일한 순서)에 대해서는 매우 비효율적 인 'O (n^2) . 효율적인 대답은 –

+0

에 대한 Jörg의 답변을 참조하십시오. 그러나 작은 배열의 경우 더 빠릅니다. – fl00r

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 : 자체 문서화 코드 : –

+0

@Joe : 저는 여전히 더 나은 방법을 찾으려하고 있습니다.당신이'each '로 할 수있는 모든 일은'inject'로 할 수 있고'Enumerable'의 모든 메소드는'each'를 기반으로한다는 것을 사소하게 증명할 수 있기 때문에'inject'를 사용하는 방법이 있어야합니다. 그러므로'Enumerable' 메쏘드의 * 어떤 조합으로도 할 수있는 모든 일은'inject'로 할 수 있습니다. 그것이 더 가독성이 있는지 여부는 또 다른 문제입니다. @ fl00r은 이미 훨씬 더 좋은 O (n²) 솔루션을 제공 했으므로 상환 된 최악의 O (n) 단계 복잡도로 유지하려고합니다. –

+0

@ Jörg 당신은 Ruby에서 O (n)과 O (n²)에 대한 링크를 줄 수 있습니까 :) – fl00r

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, 1, 6, 6] .inject ({}) {| s, v | s.merge (s [v]? {v => s [v] +1} : {v => 1})} {k, v | k if v == 1} .keys – Wes

0

배열의 각 요소에 대해 카운트를 축적하는 해시를 사용하여 펄 프로그래머에 의해 사용되는 솔루션 : 그가에 선 사이의 세미콜론의 현명한 사용에 의해, 모든 중요한 경우

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] 

그것은 나를 조금 이해하기가 어렵에 그래서, 정말 개선하지, 그리고 map{...}.compactselect{...}.keys을 대체합니다.

1

이렇게 뭔가가 필요했기 때문에 몇 가지 다른 접근법이 테스트되었습니다. 첫 번째 정수의 배열, 문자열의 다음 배열로, 10 만 반복에 대한

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 결과 :이 모든 원본 배열에서 중복 된 항목의 배열을 반환합니다. 성능이 발견 중복의 numer에에 따라 달라질 수 있지만,이 테스트는 중복 고정 수 (~ 반 배열 항목이 중복입니다)에 있습니다

이러한 접근 방식 그래서, Enumerable.dedup 알고리즘이 보인다
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. 

가장 : 원래 배열 DUP

  • 가 UNIQ 어레이 요소마다 고유 요소
  • 얻는다 불변하므로 다음 DUP 어레이를 선두 닐을
  • 결과 압축하기

(array - array.uniq)가 올바르게 작동하면! (모든 것을 제거하지 않습니다.)

관련 문제