วิธีใดที่สวยงามที่สุดในการเลือกอ็อบเจ็กต์ในอาร์เรย์ที่ไม่ซ้ำกันเมื่อเทียบกับแอตทริบิวต์อย่างน้อยหนึ่งรายการ
วัตถุเหล่านี้ถูกเก็บไว้ใน ActiveRecord ดังนั้นการใช้วิธีการของ AR ก็ใช้ได้เช่นกัน
วิธีใดที่สวยงามที่สุดในการเลือกอ็อบเจ็กต์ในอาร์เรย์ที่ไม่ซ้ำกันเมื่อเทียบกับแอตทริบิวต์อย่างน้อยหนึ่งรายการ
วัตถุเหล่านี้ถูกเก็บไว้ใน ActiveRecord ดังนั้นการใช้วิธีการของ AR ก็ใช้ได้เช่นกัน
คำตอบ:
ใช้Array#uniq
กับบล็อก:
@photos = @photos.uniq { |p| p.album_id }
require 'backports'
@photos.uniq &: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
วิธีการใน 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
จะช่วยให้เรา
ฉันไม่แน่ใจว่าคุณกำลังจะไปที่นี่เพื่ออะไร
ฉันชอบการใช้แฮชของ jmah เพื่อบังคับใช้ความเป็นเอกลักษณ์ ต่อไปนี้เป็นอีกสองวิธีในการสกินแมวตัวนั้น:
objs.inject({}) {|h,e| h[e.attr]=e; h}.values
นั่นเป็น 1 ซับที่ดี แต่ฉันสงสัยว่ามันอาจเร็วกว่าเล็กน้อย:
h = {}
objs.each {|e| h[e.attr]=e}
h.values
หากฉันเข้าใจคำถามของคุณอย่างถูกต้องฉันได้แก้ไขปัญหานี้โดยใช้วิธีการกึ่งแฮ็กในการเปรียบเทียบวัตถุ 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
วิธีที่หรูหราที่สุดที่ฉันพบคือการหมุนโดยใช้Array#uniq
บล็อก
enumerable_collection.uniq(&:property)
…อ่านดีกว่าด้วย!
คุณสามารถใช้แฮชซึ่งมีเพียงค่าเดียวสำหรับแต่ละคีย์:
Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values
ใช้Array # uniqกับบล็อก:
objects.uniq {|obj| obj.attribute}
หรือแนวทางที่รัดกุมมากขึ้น:
objects.uniq(&:attribute)
รางยังมี#uniq_by
วิธีการ
การอ้างอิง: อาร์เรย์พารามิเตอร์ # uniq (เช่น uniq_by)
ฉันชอบคำตอบของ jmah และ Head แต่พวกเขารักษาลำดับอาร์เรย์หรือไม่? พวกเขาอาจใช้ทับทิมในเวอร์ชันที่ใหม่กว่าเนื่องจากมีข้อกำหนดการเก็บรักษาแฮชที่เขียนไว้ในข้อกำหนดภาษา แต่นี่เป็นวิธีแก้ปัญหาที่คล้ายกันที่ฉันชอบใช้ซึ่งจะรักษาลำดับโดยไม่คำนึงถึง
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-attribute ที่ไม่ซ้ำกัน แต่สิ่งเดียวกันสามารถทำได้ด้วยการจัดเรียงศัพท์ ...