Soru Ruby'de nesne özniteliğine göre uniq


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

Bu nesneler ActiveRecord'da depolanır, böylece AR'ın yöntemleri de iyi olur.


104
2017-09-20 23:39


Menşei




Cevaplar:


kullanım Array#uniq bir blok ile:

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

164
2018-04-10 05:57



Bu doğru cevap ruby 1.9 ve daha sonraki versiyonlar. - nurettin
+1. Ve daha önceki Ruby'ler için her zaman require 'backports' :-) - Marc-André Lafortune
Hash_ metodu, grup_sayısını toplayarak (demek) söyleyin. - thekingoftruth
To_proc ile geliştirebilirsiniz (ruby-doc.org/core-1.9.3/Symbol.html#method-i-to_proc): @photos.uniq &:album_id - joaomilho
ve yakut 1.8 için? - brauliobo


Ekle uniq_by Projenizde Array yöntemi. İle benzeterek çalışır sort_by. Yani uniq_by yapmak uniq gibi sort_by yapmak 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 olanı değiştirmek yerine yeni bir dizi döndürdüğünü unutmayın. Yazmamışız uniq_by! yöntem ama istediyseniz yeterince kolay olmalı.

EDIT: Tribalvibes bu uygulamanın O (n ^ 2) olduğuna işaret ediyor. Daha iyi (test edilmemiş) gibi bir şey olurdu ...

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

20
2017-09-22 08:18



Güzel api ama bu büyük diziler için fakir (O ​​(n ^ 2) gibi) ölçekleme performansına sahip olacak. Dönüşümler bir hashset yaparak düzeltilebilir. - tribalvibes
Bu cevap güncel değil. Ruby> = 1.9, kabul edilen cevapta olduğu gibi tam olarak bunu yapan bir blok ile Array # uniq'e sahiptir. - Peter H. Boling


Veritabanı düzeyinde yapın:

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

13
2017-09-21 01:42



ve ya bir ilgi alanı dışında birden fazla alan olsaydı? - Ryan Bigg


Orijinal olarak kullanarak önerdim select Array yöntemi. Zekâ için:

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

Ama ilk böyle bir nesneyi isterseniz detect.

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

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


6
2017-09-20 23:45



+1 Algılama yöntemi için, bunun farkında olmayın. - pierrotlefou


Jmah'in eşsizliği sağlamak için bir Hash kullanmasını seviyorum. İşte o kediyi derlemek için birkaç yol daha:

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

Bu güzel bir linkin, ama bunun biraz daha hızlı olabileceğinden şüpheleniyorum:

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

5
2017-10-23 21:11





Diziden birkaç özellik öğesiyle benzersiz seçim yapmak için bu numarayı kullanabilirsiniz:

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

5
2018-06-08 14:21





Sorunuzu doğru bir şekilde anlarsam, herhangi bir özelliğin değişip değişmediğini saptamak için Marshaled nesnelerini karşılaştırmanın yarı-hacky yaklaşımını kullanarak bu sorunu ele aldım. Aşağıdaki kodun sonunda 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
2017-09-20 23:59





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

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

2
2017-09-21 01:48