سؤال

لديّ مجموعة من الكائنات النشر وأريد أن أكون قادرًا على فرزها بناءً على هذه الشروط:

  • أولاً ، حسب الفئة (الأخبار والأحداث والمختبرات والمحفظة وما إلى ذلك)
  • ثم حسب التاريخ ، إذا كان التاريخ ، أو عن طريق الموضع ، إذا تم تعيين فهرس محدد له

سيكون لبعض المنشورات تواريخ (الأخبار والأحداث) ، والبعض الآخر سيكون لها مواقف صريحة (المختبرات ، والمحفظة).

أريد أن أكون قادرًا على الاتصال posts.sort!, ، لذلك لقد تجاوزت <=>, ، لكنني أبحث عن الطريقة الأكثر فعالية للفرز حسب هذه الظروف. فيما يلي طريقة زائفة:

def <=>(other)
  # first, everything is sorted into 
  # smaller chunks by category
  self.category <=> other.category

  # then, per category, by date or position
  if self.date and other.date
    self.date <=> other.date
  else
    self.position <=> other.position
  end
end

يبدو أنه يجب عليّ فرز مرتين منفصلتين بالفعل ، بدلاً من حشر كل شيء في هذه الطريقة. شيء مثل sort_by_category, ، من ثم sort!. ما هي الطريقة الأكثر روبيا للقيام بذلك؟

هل كانت مفيدة؟

المحلول

يجب عليك دائمًا فرز نفس المعايير لضمان ترتيب ذي معنى. إذا مقارنة اثنين nil تواريخ ، من الجيد أن position سيحكم على الأمر ، ولكن إذا مقارنة واحد nil تاريخ مع تاريخ محدد ، عليك أن تقرر أيًا أولاً ، بغض النظر عن الموقف (على سبيل المثال عن طريق رسم الخرائط nil ليوم واحد في الماضي).

وإلا تخيل ما يلي:

a.date = nil                   ; a.position = 1
b.date = Time.now - 1.day      ; b.position = 2
c.date = Time.now              ; c.position = 0

وفقًا لمعاييرك الأصلية ، سيكون لديك: a <b <c <a. لذلك ، أي واحد هو أصغر ؟؟

تريد أيضًا أن تفعل هذا النوع مرة واحدة. من اجلك <=> التنفيذ ، والاستخدام #nonzero?:

def <=>(other)
  return nil unless other.is_a?(Post)
  (self.category <=> other.category).nonzero? ||
  ((self.date || AGES_AGO) <=> (other.date || AGES_AGO)).nonzero? ||
  (self.position <=> other.position).nonzero? ||
  0
end

إذا كنت تستخدم معايير المقارنة الخاصة بك مرة واحدة فقط ، أو إذا لم تكن هذه المعايير عالمية وبالتالي لا تريد تحديدها <=>, ، يمكنك استخدام sort مع كتلة:

post_ary.sort{|a, b| (a.category <=> ...).non_zero? || ... }

والأفضل من ذلك ، هناك sort_by و sort_by! الذي يمكنك استخدامه لإنشاء صفيف لما يمكن مقارنته في أي الأولوية:

post_ary.sort_by{|a| [a.category, a.date || AGES_AGO, a.position] }

إلى جانب كونه أقصر ، باستخدام sort_by لديه ميزة أنه يمكنك فقط الحصول على معايير أمر جيد.

ملاحظات:

  • sort_by! تم تقديمه في Ruby 1.9.2. تستطيع require 'backports/1.9.2/array/sort_by' لاستخدامه مع الياقوت الأقدم.
  • أنا أفترض ذلك Post ليست فئة فرعية من ActiveRecord::Base (في هذه الحالة ، تريد أن يتم التصنيف بواسطة خادم DB).

نصائح أخرى

بدلاً من ذلك ، يمكنك القيام بالفرز في ضربة واحدة في صفيف ، فإن Gotcha الوحيد هو التعامل مع الحالة التي تكون فيها إحدى السمات لا شيء ، على الرغم من أنه لا يزال من الممكن التعامل معها إذا كنت تعرف البيانات التي تم تعيينها عن طريق تحديد حارس NIL المناسب. كما أنه ليس واضحًا من رمز PSUEDO الخاص بك إذا تم سرد مقارنات التاريخ والموقف في أمر الأولوية أو أحد أو آخر (أي تاريخ استخدام إذا كان موجودًا لكلا الاستخدام الآخر). الحل الأول يفترض الاستخدام ، الفئة ، تليها التاريخ ، تليها الموقف

def <=>(other)
    [self.category, self.date, self.position] <=> [other.category, other.date, other.position]
end

الثاني يفترض أنه تاريخ أو موقف

def <=>(other)
    if self.date && other.date
        [self.category, self.date] <=> [other.category, other.date]
    else
        [self.category, self.position] <=> [other.category, other.position]
    end
end
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top