Uniq โดย object attribute ใน Ruby


127

วิธีใดที่สวยงามที่สุดในการเลือกอ็อบเจ็กต์ในอาร์เรย์ที่ไม่ซ้ำกันเมื่อเทียบกับแอตทริบิวต์อย่างน้อยหนึ่งรายการ

วัตถุเหล่านี้ถูกเก็บไว้ใน ActiveRecord ดังนั้นการใช้วิธีการของ AR ก็ใช้ได้เช่นกัน

คำตอบ:


201

ใช้Array#uniqกับบล็อก:

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

5
นี่คือคำตอบที่ถูกต้องสำหรับRuby 1.9และเวอร์ชันที่ใหม่กว่า
nurettin

2
+1 และสำหรับrequire 'backports'
รูบี้

วิธีแฮชจะดีกว่าถ้าคุณต้องการจัดกลุ่มโดยพูดว่า album_id ในขณะที่ (พูด) สรุป num_plays
thekingoftruth

20
คุณสามารถปรับปรุงได้ด้วย to_proc ( ruby-doc.org/core-1.9.3/Symbol.html#method-i-to_proc ):@photos.uniq &:album_id
joaomilho

@brauliobo สำหรับ Ruby 1.8 คุณต้องอ่านด้านล่างใน SO เดียวกันนี้: stackoverflow.com/a/113770/213191
Peter H. Boling

22

เพิ่ม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

1
API ที่ดี แต่จะมีประสิทธิภาพในการปรับขนาดไม่ดี (ดูเหมือน O (n ^ 2)) สำหรับอาร์เรย์ขนาดใหญ่ สามารถแก้ไขได้โดยการแปลงแฮชเซ็ต
tribalvibes

7
คำตอบนี้ล้าสมัย Ruby> = 1.9 มี Array # uniq พร้อมกับบล็อกที่ทำเช่นนี้ตามคำตอบที่ยอมรับ
ปีเตอร์เอชโบลิง

17

ทำในระดับฐานข้อมูล:

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

1
แล้วถ้าเป็นมากกว่าหนึ่งเขตข้อมูลโดยไม่สนใจล่ะ?
Ryan Bigg

12

คุณสามารถใช้เคล็ดลับนี้เพื่อเลือกเฉพาะโดยองค์ประกอบแอตทริบิวต์ต่างๆจากอาร์เรย์:

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

เห็นได้ชัดเลยทับทิม อีกเหตุผลหนึ่งที่จะอวยพร Ruby
ToTenMilan

6

ฉันได้แนะนำให้ใช้ไฟล์ selectวิธีการใน Array เพื่อปัญญา:

[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จะช่วยให้เรา

ฉันไม่แน่ใจว่าคุณกำลังจะไปที่นี่เพื่ออะไร


5

ฉันชอบการใช้แฮชของ jmah เพื่อบังคับใช้ความเป็นเอกลักษณ์ ต่อไปนี้เป็นอีกสองวิธีในการสกินแมวตัวนั้น:

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

นั่นเป็น 1 ซับที่ดี แต่ฉันสงสัยว่ามันอาจเร็วกว่าเล็กน้อย:

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

3

หากฉันเข้าใจคำถามของคุณอย่างถูกต้องฉันได้แก้ไขปัญหานี้โดยใช้วิธีการกึ่งแฮ็กในการเปรียบเทียบวัตถุ Marshaled เพื่อพิจารณาว่าคุณสมบัติใด ๆ ที่แตกต่างกันหรือไม่ การฉีดที่ส่วนท้ายของรหัสต่อไปนี้จะเป็นตัวอย่าง:

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

วิธีที่หรูหราที่สุดที่ฉันพบคือการหมุนโดยใช้Array#uniqบล็อก

enumerable_collection.uniq(&:property)

…อ่านดีกว่าด้วย!


2

คุณสามารถใช้แฮชซึ่งมีเพียงค่าเดียวสำหรับแต่ละคีย์:

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



1

ฉันชอบคำตอบของ jmah และ Head แต่พวกเขารักษาลำดับอาร์เรย์หรือไม่? พวกเขาอาจใช้ทับทิมในเวอร์ชันที่ใหม่กว่าเนื่องจากมีข้อกำหนดการเก็บรักษาแฮชที่เขียนไว้ในข้อกำหนดภาษา แต่นี่เป็นวิธีแก้ปัญหาที่คล้ายกันที่ฉันชอบใช้ซึ่งจะรักษาลำดับโดยไม่คำนึงถึง

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


0

ตอนนี้ถ้าคุณสามารถจัดเรียงค่าแอตทริบิวต์สิ่งนี้สามารถทำได้:

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-attribute ที่ไม่ซ้ำกัน แต่สิ่งเดียวกันสามารถทำได้ด้วยการจัดเรียงศัพท์ ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.