سؤال

ما هي الطريقة الأكثر أناقة لتحديد الكائنات الموجودة في مصفوفة والتي تكون فريدة من نوعها فيما يتعلق بواحدة أو أكثر من السمات؟

يتم تخزين هذه الكائنات في 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 فريدة من نوعها، ولكن يمكن فعل الشيء نفسه مع الفرز المعجمي ...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top