Uniq حسب سمة الكائن في روبي
-
02-07-2019 - |
سؤال
ما هي الطريقة الأكثر أناقة لتحديد الكائنات الموجودة في مصفوفة والتي تكون فريدة من نوعها فيما يتعلق بواحدة أو أكثر من السمات؟
يتم تخزين هذه الكائنات في ActiveRecord لذا فإن استخدام أساليب الواقع المعزز سيكون أمرًا جيدًا أيضًا.
المحلول
يستخدم Array#uniq
مع كتلة:
@photos = @photos.uniq { |p| p.album_id }
نصائح أخرى
أضف ال uniq_by
طريقة للصفيف في مشروعك.وهو يعمل عن طريق القياس مع 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
.
لست متأكدًا مما ستفعله هنا، رغم ذلك.
يعجبني استخدام جمعة للتجزئة لفرض التفرد.إليك طريقتان إضافيتان لسلخ تلك القطة:
objs.inject({}) {|h,e| h[e.attr]=e; h}.values
هذه سطر واحد لطيف، لكنني أظن أن هذا قد يكون أسرع قليلاً:
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 - راجع صفيف ذو معلمات #uniq (أي، uniq_by)
أنا أحب أجوبة جمعة ورئيس.لكن هل يحافظون على ترتيب المصفوفة؟قد يكون ذلك في الإصدارات الأحدث من روبي نظرًا لوجود بعض متطلبات الحفاظ على ترتيب إدراج التجزئة المكتوبة في مواصفات اللغة، ولكن إليك حلًا مشابهًا أحب استخدامه والذي يحافظ على النظام بغض النظر.
h = Set.new
objs.select{|el| h.add?(el.attr)}
تنفيذ الدعم النشط:
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 فريدة من نوعها، ولكن يمكن فعل الشيء نفسه مع الفرز المعجمي ...