الفرز حسب ظروف متعددة في روبي
-
26-09-2019 - |
سؤال
لديّ مجموعة من الكائنات النشر وأريد أن أكون قادرًا على فرزها بناءً على هذه الشروط:
- أولاً ، حسب الفئة (الأخبار والأحداث والمختبرات والمحفظة وما إلى ذلك)
- ثم حسب التاريخ ، إذا كان التاريخ ، أو عن طريق الموضع ، إذا تم تعيين فهرس محدد له
سيكون لبعض المنشورات تواريخ (الأخبار والأحداث) ، والبعض الآخر سيكون لها مواقف صريحة (المختبرات ، والمحفظة).
أريد أن أكون قادرًا على الاتصال 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