루비의 해시 배열에서 고유 한 요소를 어떻게 얻습니까?
문제
해시 배열이 있고 고유 한 값을 원합니다. 부름 Array.uniq
내가 기대하는 것을주지 않습니다.
a = [{:a => 1},{:a => 2}, {:a => 1}]
a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}]
내가 예상 한 곳 :
[{:a => 1}, {:a => 2}]
그물을 찾아서, 나는 내가 행복한 솔루션을 생각해 내지 않았다. 사람들은 재정의를 추천했습니다 Hash.eql?
그리고 Hash.hash
, 그 이유는 무엇이기 때문입니다 Array.uniq
쿼리입니다.
편집 : 현실 세계에서 이것에 부딪친 곳에서 해시는 약간 더 복잡했습니다. 그것들은 여러 필드를 가진 구문 분석 JSON의 결과였으며, 그 중 일부는 값도 해시였습니다. 고유 한 값을 필터링하고 싶었던 결과의 배열이있었습니다.
나는 재정의를 좋아하지 않는다 Hash.eql?
그리고 Hash.hash
해결책, 재정의해야하기 때문에 Hash
내 배열의 각 항목에 대해 전 세계적으로 재정의하십시오. 정의를 변경합니다 Hash
각 항목의 경우 각 항목의 내부에 중첩 된 해시가있을 수 있기 때문에 각 항목은 번거 롭습니다.
바꾸다 Hash
전 세계에는 특히 일시적으로 수행 된 경우 잠재력이 있습니다. 나는 이전 정의를 저장하고 복원하는 다른 클래스 또는 도우미 기능을 구축하고 싶지만, 이것이 실제로 필요한 것보다 더 복잡하다고 생각합니다.
사용 inject
재정의에 대한 좋은 대안 인 것 같습니다 Hash
.
해결책
나는 전화를 통해 내가 원하는 것을 얻을 수 있습니다 inject
a = [{:a => 1},{:a => 2}, {:a => 1}]
a.inject([]) { |result,h| result << h unless result.include?(h); result }
이것은 돌아올 것입니다 :
[{:a=>1}, {:a=>2}]
다른 팁
루비 1.8.7+는 예상 한 내용을 반환합니다.
[{:a=>1}, {:a=>2}, {:a=>1}].uniq
#=> [{:a=>1}, {:a=>2}]
나는 비슷한 상황이 있었지만 해시는 열쇠를 가지고있었습니다. 분류 방법을 사용했습니다.
내 말은:
배열이 있습니다.
[{:x=>1},{:x=>2},{:x=>3},{:x=>2},{:x=>1}]
당신은 그것을 분류합니다 (#sort_by {|t| t[:x]}
) 그리고 이것을 얻으십시오 :
[{:x=>1}, {:x=>1}, {:x=>2}, {:x=>2}, {:x=>3}]
이제 Aaaron Hinni의 약간 수정 된 답변 버전 :
your_array.inject([]) do |result,item|
result << item if !result.last||result.last[:x]!=item[:x]
result
end
나는 또한 시도했다 :
test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]}
그러나 매우 느립니다. 다음은 내 벤치 마크입니다.
test=[]
1000.times {test<<{:x=>rand}}
Benchmark.bmbm do |bm|
bm.report("sorting: ") do
test.sort_by {|t| t[:x]}.inject([]) {|r,h| r<<h if !r.last||r.last[:x]!=h[:x]; r}
end
bm.report("inject: ") {test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]} }
end
결과:
Rehearsal ---------------------------------------------
sorting: 0.010000 0.000000 0.010000 ( 0.005633)
inject: 0.470000 0.140000 0.610000 ( 0.621973)
------------------------------------ total: 0.620000sec
user system total real
sorting: 0.010000 0.000000 0.010000 ( 0.003839)
inject: 0.480000 0.130000 0.610000 ( 0.612438)
해시가 항상 단일 키 값 쌍이라고 가정하면 다음이 작동합니다.
a.map {|h| h.to_a[0]}.uniq.map {|k,v| {k => v}}
hash.to_a는 키 값 배열 배열을 만듭니다. 따라서 첫 번째 맵은 다음을 얻습니다.
[[:a, 1], [:a, 2], [:a, 1]]
Uniq On Array는 원하는 것을 수행하여 다음을 제공합니다.
[[:a, 1], [:a, 2]]
그런 다음 두 번째 맵은 다시 해시로 다시 합류합니다.
사용할 수 있습니다 (Ruby 1.9.3에서 테스트),
[{a: 1},{a: 2},{a:1}].uniq => [{a:1},{a: 2}]
[{a: 1,b: 2},{a: 2, b: 2},{a: 1, b: 3}].uniq_by {|v| v[:a]} => [{a: 1,b: 2},{a: 2, b: 2}]
당신이주는 대답은 논의 된 것과 유사합니다. 여기. 그것은 그것을 무시합니다 hash
그리고 eql?
배열에 나타나야하는 해시의 메소드 uniq
올바르게 행동하십시오.
배열의 파이프 메소드 (1.8.6 이후 사용 가능)는 Set Union (배열 반환)을 수행하므로 다음은 모든 배열의 고유 한 요소를 얻을 수있는 또 다른 방법입니다. a
:
[] | a