Ruby'de nesne özelliğine göre Uniq


127

Bir dizide bir veya daha fazla öznitelik açısından benzersiz olan nesneleri seçmenin en zarif yolu nedir?

Bu nesneler ActiveRecord'da saklanır, bu nedenle AR yöntemlerinin kullanılması da iyi olur.

Yanıtlar:


201

Array#uniqBir blokla kullanın :

@photos = @photos.uniq { |p| p.album_id }

5
Ruby 1.9 ve sonraki sürümler için doğru cevap budur .
nurettin

2
+1. Ve daha önceki require 'backports'
Ruby'ler için

Num_plays toplarken (diyelim) album_id diyerek gruplamak istiyorsanız, hash yöntemi daha iyidir.
thekingoftruth

20
Bunu bir to_proc ( ruby-doc.org/core-1.9.3/Symbol.html#method-i-to_proc ) ile geliştirebilirsiniz :@photos.uniq &:album_id
joaomilho

Eğer gerekmez Ruby 1.8 için @brauliobo aynı SO sadece aşağıda okumak için: stackoverflow.com/a/113770/213191
Peter H. Boling

22

Ekle uniq_byprojenizde Array yöntemi. İle benzetme yaparak çalışır sort_by. Yani uniq_byetmektir uniqolarak sort_byetmektir sort. Kullanımı:

uniq_array = my_array.uniq_by {|obj| obj.id}

Hayata geçirme:

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

Mevcut dizinizi yerinde değiştirmek yerine yeni bir dizi döndürdüğünü unutmayın. Henüz bir uniq_by!yöntem yazmadık ama istersen yeterince kolay olmalı.

DÜZENLEME: Tribalvibes, uygulamanın O (n ^ 2) olduğuna işaret ediyor. Daha iyi bir şey (denenmemiş) ...

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

1
Güzel api ama bu, büyük diziler için zayıf (O (n ^ 2) gibi görünüyor) ölçeklendirme performansına sahip olacak. Dönüşümleri bir hashset yapılarak düzeltilebilir.
tribalvibes

7
Bu cevap güncel değil. Ruby> = 1.9, kabul edilen cevapta olduğu gibi tam olarak bunu yapan bir bloğa sahip Array # uniq'e sahiptir.
Peter H. Boling

17

Bunu veritabanı düzeyinde yapın:

YourModel.find(:all, :group => "status")

1
ve ya birden fazla alan ilgi alanı dışıysa?
Ryan Bigg

12

Bu numarayı, dizideki birkaç öznitelik elemanına göre benzersiz seçmek için kullanabilirsiniz:

@photos = @photos.uniq { |p| [p.album_id, p.author_id] }

çok açık, çok Ruby. Ruby'yi kutsamak için başka bir neden
ToTenMilan

6

Başlangıçta selectArray yöntemini kullanmayı önermiştim . Zekaya:

[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0} bize [2,4,6]geri verir .

Ancak böyle ilk nesneyi istiyorsanız, kullanın detect.

[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3}bize verir 4.

Yine de burada ne için gideceğinden emin değilim.


5

Jmah'ın benzersizliği sağlamak için Hash kullanmasını seviyorum. İşte o kedinin derisini yüzmenin birkaç yolu daha:

objs.inject({}) {|h,e| h[e.attr]=e; h}.values

Bu güzel bir 1 hat, ancak bunun biraz daha hızlı olabileceğinden şüpheleniyorum:

h = {}
objs.each {|e| h[e.attr]=e}
h.values

3

Sorunuzu doğru anlarsam, bu sorunu, herhangi bir özniteliğin farklılık gösterip göstermediğini belirlemek için Marshalled nesneleri karşılaştırmanın yarı-hacky yaklaşımını kullanarak çözdüm. Aşağıdaki kodun sonundaki enjekte bir örnek olacaktır:

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

3

Bulduğum en zarif yol Array#uniq, bir blok ile kullanmaktır.

enumerable_collection.uniq(&:property)

… O da daha iyi okuyor!


2

Her anahtar için yalnızca bir değer içeren bir karma kullanabilirsiniz:

Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values



1

Jmah ve Head'in cevaplarını seviyorum. Ama dizi sırasını koruyorlar mı? Ruby'nin sonraki sürümlerinde, dil spesifikasyonuna yazılan bazı hash ekleme-siparişi koruma gereksinimleri olduğu için bunlar olabilir, ancak burada, ne olursa olsun düzeni koruyan kullanmaktan hoşlandığım benzer bir çözüm var.

h = Set.new
objs.select{|el| h.add?(el.attr)}

1

ActiveSupport uygulaması:

def uniq_by
  hash, array = {}, []
  each { |i| hash[yield(i)] ||= (array << i) }
  array
end

0

Şimdi, öznitelik değerlerine göre sıralama yapabiliyorsanız, bu yapılabilir:

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

Bu, 1 özellikli benzersizdir, ancak aynı şey sözlükbilimsel sıralama ile de yapılabilir ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.