Ruby의 객체 속성별 Uniq
-
02-07-2019 - |
문제
하나 이상의 속성과 관련하여 고유한 배열의 개체를 선택하는 가장 우아한 방법은 무엇입니까?
이러한 개체는 ActiveRecord에 저장되므로 AR의 방법을 사용하는 것도 좋습니다.
해결책
사용 Array#uniq
블록 포함:
@photos = @photos.uniq { |p| p.album_id }
다른 팁
추가 uniq_by
프로젝트에서 Array 메소드를 사용하세요.이는 다음과 유사하게 작동합니다. sort_by
.그래서 uniq_by
이다 uniq
~처럼 sort_by
이다 sort
.용법:
uniq_array = my_array.uniq_by {|obj| obj.id}
구현:
class Array
def uniq_by(&blk)
transforms = []
self.select do |el|
should_keep = !transforms.include?(t=blk[el])
transforms << t
should_keep
end
end
end
현재 배열을 수정하는 대신 새 배열을 반환한다는 점에 유의하세요.우리는 글을 쓰지 않았습니다 uniq_by!
하지만 원한다면 충분히 쉬울 것입니다.
편집하다:Tribalvibes는 해당 구현이 O(n^2)임을 지적합니다.(테스트되지 않은) 것과 같은 것이 더 좋을 것입니다 ...
class Array
def uniq_by(&blk)
transforms = {}
select do |el|
t = blk[el]
should_keep = !transforms[t]
transforms[t] = true
should_keep
end
end
end
데이터베이스 수준에서 수행하십시오.
YourModel.find(:all, :group => "status")
이 트릭을 사용하여 배열에서 여러 속성 요소로 고유한 요소를 선택할 수 있습니다.
@photos = @photos.uniq { |p| [p.album_id, p.author_id] }
나는 원래 select
배열의 메서드.재치있게 말하자면:
[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0}
우리에게 주어지다 [2,4,6]
뒤쪽에.
그러나 첫 번째 객체를 원한다면 다음을 사용하십시오. detect
.
[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3}
우리에게 주어지다 4
.
하지만 당신이 여기서 무엇을 하려는 것인지 잘 모르겠습니다.
나는 고유성을 강화하기 위해 jmah가 해시를 사용하는 것을 좋아합니다.고양이의 가죽을 벗기는 몇 가지 방법은 다음과 같습니다.
objs.inject({}) {|h,e| h[e.attr]=e; h}.values
그것은 좋은 1-liner이지만 이것이 조금 더 빠를 것이라고 생각합니다.
h = {}
objs.each {|e| h[e.attr]=e}
h.values
귀하의 질문을 올바르게 이해했다면 마샬링된 개체를 비교하여 속성이 다른지 확인하는 준해키 접근 방식을 사용하여 이 문제를 해결했습니다.다음 코드 끝에 있는 삽입은 예입니다.
class Foo
attr_accessor :foo, :bar, :baz
def initialize(foo,bar,baz)
@foo = foo
@bar = bar
@baz = baz
end
end
objs = [Foo.new(1,2,3),Foo.new(1,2,3),Foo.new(2,3,4)]
# find objects that are uniq with respect to attributes
objs.inject([]) do |uniqs,obj|
if uniqs.all? { |e| Marshal.dump(e) != Marshal.dump(obj) }
uniqs << obj
end
uniqs
end
내가 찾은 가장 우아한 방법은 다음을 사용하는 스핀오프입니다. Array#uniq
블록으로
enumerable_collection.uniq(&:property)
…읽기도 더 좋습니다!
각 키에 대해 하나의 값만 포함하는 해시를 사용할 수 있습니다.
Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values
Rails에는 #uniq_by 메소드도 있습니다 - 참조 매개변수화된 Array#uniq(예: uniq_by)
나는 jmah와 Head의 답변을 좋아합니다.하지만 배열 순서가 유지되나요?언어 사양에 해시 삽입 순서 유지 요구 사항이 기록되어 있기 때문에 최신 버전의 Ruby에서는 그럴 수도 있지만 여기에 관계없이 순서를 유지하는 데 사용하고 싶은 유사한 솔루션이 있습니다.
h = Set.new
objs.select{|el| h.add?(el.attr)}
ActiveSupport 구현:
def uniq_by
hash, array = {}, []
each { |i| hash[yield(i)] ||= (array << i) }
array
end
이제 속성 값을 정렬할 수 있으면 다음과 같이 할 수 있습니다.
class A
attr_accessor :val
def initialize(v); self.val = v; end
end
objs = [1,2,6,3,7,7,8,2,8].map{|i| A.new(i)}
objs.sort_by{|a| a.val}.inject([]) do |uniqs, a|
uniqs << a if uniqs.empty? || a.val != uniqs.last.val
uniqs
end
이는 1속성 고유에 대한 것이지만 사전순 정렬로도 동일한 작업을 수행할 수 있습니다.