ย้อนกลับลำดับการจัดเรียงด้วย Backbone.js


90

ด้วยBackbone.jsฉันได้ตั้งค่าคอลเลคชันด้วยฟังก์ชันเปรียบเทียบ มันจัดเรียงแบบจำลองได้ดี แต่ฉันต้องการกลับลำดับ

ฉันจะจัดเรียงแบบจำลองจากมากไปหาน้อยได้อย่างไร


2
สำหรับการเรียงลำดับย้อนกลับบน Strings (รวมถึงฟิลด์ created_at) ดูคำตอบนี้สำหรับคำถามอื่น
Eric Hu

2
มีการรองรับการเปรียบเทียบแบบเรียงลำดับตั้งแต่ต้นปี 2555 เพียงยอมรับ 2 อาร์กิวเมนต์และส่งคืน -1, 0 หรือ 1. github.com/documentcloud/backbone/commit/…
geon

นี่คือสิ่งที่ใช้ได้ผลสำหรับฉัน (Strings and Numbers): stackoverflow.com/questions/5013819/…
Fasani

คำตอบ:


133

คุณสามารถคืนค่าลบจากตัวเปรียบเทียบได้ ตัวอย่างเช่นหากเราใช้ตัวอย่างจากไซต์ของ Backbone และต้องการย้อนลำดับจะมีลักษณะดังนี้:

var Chapter  = Backbone.Model;
var chapters = new Backbone.Collection;

chapters.comparator = function(chapter) {
  return -chapter.get("page"); // Note the minus!
};

chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));

alert(chapters.pluck('title'));

3
hi5 สำหรับการใช้โค้ดตัวอย่างเดียวกันกับของฉัน! และความแตกต่างภายใน 15 วินาที เหมือนใจฉันต้องพูด.
DhruvPathak

อ๋อแน่นอนแค่เครื่องหมายลบ ขอบคุณทั้งสองสำหรับคำตอบด่วน!
Drew Dara-Abrams

นอกจากนี้โปรดทราบว่า (ตามที่กล่าวไว้ในไซต์ของ Backbone) สิ่งนี้จะแตกหากคุณเปลี่ยนแอตทริบิวต์ของโมเดล (หากคุณเพิ่มแอตทริบิวต์ "length" ลงในบทเป็นต้น) ในกรณีนี้คุณจะต้องเรียก "sort ()" บนคอลเล็กชันด้วยตนเอง
cmcculloh

9
มีการรองรับการเปรียบเทียบแบบเรียงลำดับตั้งแต่ต้นปี 2555 เพียงยอมรับ 2 อาร์กิวเมนต์และส่งคืน -1, 0 หรือ 1. github.com/documentcloud/backbone/commit/…
geon

การตอบกลับของ @Fasani ใช้ได้กับประเภทอื่นที่ไม่ใช่ตัวเลขโปรดดู: stackoverflow.com/questions/5013819/…
JayC

56

โดยส่วนตัวแล้วฉันไม่ค่อยพอใจกับวิธีแก้ปัญหาใด ๆ ที่ให้ไว้ที่นี่:

  • โซลูชันการคูณด้วย -1 ล้มเหลวในการทำงานเมื่อประเภทการจัดเรียงไม่ใช่ตัวเลข แม้ว่าจะมีวิธีการต่างๆ (โดยการเรียกDate.getTime()เช่น) กรณีเหล่านี้เป็นกรณีเฉพาะ ฉันต้องการวิธีทั่วไปในการย้อนกลับทิศทางของการเรียงลำดับใด ๆ โดยไม่ต้องกังวลเกี่ยวกับประเภทของฟิลด์ที่ระบุ

  • สำหรับโซลูชันสตริงการสร้างสตริงทีละอักขระดูเหมือนว่าเป็นคอขวดของประสิทธิภาพโดยเฉพาะอย่างยิ่งเมื่อใช้คอลเลคชันขนาดใหญ่ (หรือสตริงฟิลด์การเรียงลำดับขนาดใหญ่)

นี่คือโซลูชันที่ใช้งานได้ดีสำหรับฟิลด์สตริงวันที่และตัวเลข:

ประการแรกประกาศตัวเปรียบเทียบที่จะย้อนกลับผลลัพธ์ของผลลัพธ์ของฟังก์ชัน sortBy ดังนี้:

function reverseSortBy(sortByFunction) {
  return function(left, right) {
    var l = sortByFunction(left);
    var r = sortByFunction(right);

    if (l === void 0) return -1;
    if (r === void 0) return 1;

    return l < r ? 1 : l > r ? -1 : 0;
  };
}

ตอนนี้หากคุณต้องการกลับทิศทางของการจัดเรียงสิ่งที่เราต้องทำคือ:

var Chapter  = Backbone.Model;
var chapters = new Backbone.Collection;

// Your normal sortBy function
chapters.comparator = function(chapter) {
  return chapter.get("title"); 
};


// If you want to reverse the direction of the sort, apply 
// the reverseSortBy function.
// Assuming the reverse flag is kept in a boolean var called reverseDirection 
if(reverseDirection) {
   chapters.comparator = reverseSortBy(chapters.comparator);
}

chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));

alert(chapters.pluck('title'));

เหตุผลนี้ทำงานได้Backbone.Collection.sortแตกต่างกันถ้าฟังก์ชัน sort มีสองอาร์กิวเมนต์ Array.sortในกรณีนี้มันจะทำงานในลักษณะเดียวกับที่เปรียบเทียบผ่านเข้ามา วิธีการแก้ปัญหานี้ทำงานโดยการปรับฟังก์ชัน sortBy อาร์กิวเมนต์เดียวเป็นตัวเปรียบเทียบอาร์กิวเมนต์สองตัวและกลับผลลัพธ์


7
ในฐานะผู้ที่มีคำตอบที่ได้รับการโหวตมากที่สุดสำหรับคำถามนี้ฉันชอบแนวทางนี้มากและคิดว่าน่าจะมีการโหวตเพิ่มขึ้น
Andrew De Andrade

ขอบคุณมาก Andrew
Andrew Newdigate

ดีมากฉันลงเอยด้วยการปรับแต่งเล็กน้อยและทำให้มันเป็นนามธรรมใน Backbone ต้นแบบของคอลเลกชัน ขอบคุณมาก!
Kendrick Ledet

46

ตัวเปรียบเทียบคอลเล็กชันของ Backbone.js อาศัยวิธี Underscore.js _.sortBy วิธีการใช้ sortBy จะลงเอยด้วยการ "ตัด" javascript .sort () ในลักษณะที่ทำให้การเรียงลำดับสตริงย้อนกลับทำได้ยาก การปฏิเสธอย่างง่ายของสตริงจะจบลงด้วยการส่งคืน NaN และหยุดการเรียงลำดับ

หากคุณต้องการทำการเรียงลำดับย้อนกลับด้วย Strings เช่นการเรียงลำดับตัวอักษรย้อนกลับนี่เป็นวิธีที่แฮ็กจริงๆ:

comparator: function (Model) {
  var str = Model.get("name");
  str = str.toLowerCase();
  str = str.split("");
  str = _.map(str, function(letter) { 
    return String.fromCharCode(-(letter.charCodeAt(0)));
  });
  return str;
}

มันไม่ได้สวยอะไร แต่มันคือ "ตัวลบสตริง" หากคุณไม่มีคุณสมบัติใด ๆ กับการแก้ไขประเภทออบเจ็กต์เนทีฟในจาวาสคริปต์คุณสามารถทำให้โค้ดของคุณชัดเจนขึ้นได้โดยการแยกตัวลบสตริงและเพิ่มเป็นวิธีการใน String.Prototype อย่างไรก็ตามคุณอาจต้องการทำสิ่งนี้ก็ต่อเมื่อคุณรู้ว่าคุณกำลังทำอะไรอยู่เนื่องจากการแก้ไขประเภทวัตถุดั้งเดิมในจาวาสคริปต์อาจส่งผลที่ไม่คาดคิดได้


สิ่งนี้มีประโยชน์มาก ขอบคุณ Andrew!
Abel

1
ไม่มีพร็อบ ฉันแค่จะเพิ่มว่าคุณควรถามตัวเองว่าทำไมคุณถึงใช้การเรียงลำดับตัวอักษรย้อนกลับและอาจมีวิธีแก้ปัญหาที่หรูหรากว่านี้จากมุมมองของประสบการณ์ผู้ใช้
Andrew De Andrade

1
@AndrewDeAndrade เหตุผล: ฉันต้องการจัดเรียงคอลเล็กชันตามแอตทริบิวต์ "created_at" โดยให้บริการชุดใหม่ล่าสุดก่อน แอตทริบิวต์นี้เป็นหนึ่งในค่าเริ่มต้นของ Backbone และถูกจัดเก็บเป็น String คำตอบของคุณใกล้เคียงกับสิ่งที่ฉันต้องการมากที่สุด ขอบคุณ!
Eric Hu

นี่เป็นเรื่องที่น่าทึ่งมากที่ได้ดำเนินการแก้ไขปัญหานี้ในช่วงหนึ่งชั่วโมงที่ผ่านมา

29

วิธีแก้ปัญหาของฉันคือการย้อนกลับผลลัพธ์หลังจากเรียงลำดับ

เพื่อป้องกันการแสดงผลซ้ำสองครั้งในการจัดเรียงครั้งแรกโดยไม่มีการโต้ตอบจากนั้นทริกเกอร์ 'รีเซ็ต'

collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});

2
ทางออกที่ดีกว่าคือการลบล้างผลลัพธ์ของตัวเปรียบเทียบ
Daniel X Moore

7
Daniel ซึ่งเป็นไปไม่ได้เสมอไปเช่นเมื่อจัดเรียงสตริงหรือวันที่
Gavin Schulz

1
แน่นอนว่าเป็น สำหรับวันที่คุณสามารถส่งคืนตัวเปรียบเทียบได้-Date.getTime()โดยอาจมีการแฮ็กเพิ่มเติมหากคุณเชื่อว่าวันที่ในระบบของคุณจะข้ามยุคยูนิกซ์ สำหรับการเรียงตามตัวอักษรให้ดูที่คำตอบของ Andrew ด้านบน
asparagino

@DanielXMoore ทำไมถึงต้องเป็นทางออกที่ดีกว่านี้? เร็วกว่ามั้ย? โดยส่วนตัวแล้วฉันพบว่ามันง่ายมากที่จะจัดเก็บแอตทริบิวต์ที่ผู้ใช้จัดเรียงคอลเลคชันครั้งล่าสุดจากนั้นย้อนกลับการจัดเรียงปัจจุบันหากเขาจัดเรียงตามแอตทริบิวต์นั้นอีกครั้ง (เพื่อใช้พฤติกรรมการเรียงลำดับที่คุณเห็นในตาราง Wikipedia ซึ่งสามารถสลับทิศทางการจัดเรียงได้โดยการคลิกแอตทริบิวต์ซ้ำ ๆ ในส่วนหัวของตาราง) ด้วยวิธีนี้ฉันจะรักษาตรรกะของตัวเปรียบเทียบให้สะอาดและช่วยตัวเองในการจัดเรียงหากเราเปลี่ยนจาก Asc to Desc sort order
Mansiemans

13

ปรับเปลี่ยนฟังก์ชันตัวเปรียบเทียบของคุณเพื่อส่งกลับค่าโพรโพลิชันแบบย้อนกลับแทนการส่งคืนข้อมูลที่คุณอยู่ในปัจจุบัน โค้ดบางส่วนจาก: http://documentcloud.github.com/backbone/#Collection-comparator

ตัวอย่าง:

var Chapter  = Backbone.Model;
var chapters = new Backbone.Collection;

/* Method 1: This sorts by page number */
chapters.comparator = function(chapter) {
  return chapter.get("page");
};

/* Method 2: This sorts by page number in reverse */
chapters.comparator = function(chapter) {
  return -chapter.get("page");
};

chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));

9

สิ่งต่อไปนี้ใช้ได้ผลดีสำหรับฉัน:

comparator: function(a, b) {
  // Optional call if you want case insensitive
  name1 = a.get('name').toLowerCase();
  name2 = b.get('name').toLowerCase();

  if name1 < name2
    ret = -1;
  else if name1 > name2
    ret = 1;
  else
    ret = 0;

  if this.sort_dir === "desc"
    ret = -ret
  return ret;
}

collection.sort_dir = "asc";
collection.sort(); // returns collection in ascending order
collection.sort_dir = "desc";
collection.sort(); // returns collection in descending order

8

ฉันอ่านบางส่วนที่ฟังก์ชันเปรียบเทียบ Backbone ใช้หรือเลียนแบบฟังก์ชัน JavaScript Array.prototype.sort () ซึ่งหมายความว่าจะใช้ 1, -1 หรือ 0 เพื่อตัดสินใจเกี่ยวกับลำดับการจัดเรียงของแต่ละรุ่นในคอลเล็กชัน สิ่งนี้อัปเดตอย่างต่อเนื่องและคอลเลคชันจะอยู่ในลำดับที่ถูกต้องตามตัวเปรียบเทียบ

ข้อมูลเกี่ยวกับ Array.prototype.sort () สามารถพบได้ที่นี่: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en-US&redirectslug=JavaScript% 2FReference% 2FGlobal_Objects% 2FArray% 2Fsort

คำตอบจำนวนมากสำหรับคำถามนี้เพียงแค่ย้อนลำดับก่อนที่จะส่งไปที่หน้า ไม่เหมาะอย่างยิ่ง

การใช้บทความข้างต้นเกี่ยวกับ Mozilla เป็นแนวทางฉันสามารถจัดเรียงคอลเลคชันของฉันในลำดับย้อนกลับได้โดยใช้รหัสต่อไปนี้จากนั้นเราก็แสดงคอลเลกชันเป็นหน้าตามลำดับปัจจุบันและเราสามารถใช้แฟล็ก (reverseSortDirection) เพื่อย้อนกลับคำสั่ง .

//Assuming this or similar will be in your Backbone.Collection.
sortKey: 'id', //The field name of the item in your collection

reverseSortDirection: false,

comparator: function(a, b) {

  var sampleDataA = a.get(this.sortKey),
      sampleDataB = b.get(this.sortKey);

      if (this.reverseSortDirection) {
        if (sampleDataA > sampleDataB) { return -1; }
        if (sampleDataB > sampleDataA) { return 1; }
        return 0;
      } else {
        if (sampleDataA < sampleDataB) { return -1; }
        if (sampleDataB < sampleDataA) { return 1; }
        return 0;
      }

},

ตัวเปรียบเทียบยกเว้นสองรุ่นในการเปลี่ยนแปลงคอลเลกชันหรือโมเดลที่ฟังก์ชันตัวเปรียบเทียบทำงานและเปลี่ยนลำดับของคอลเลกชัน

ฉันได้ทดสอบแนวทางนี้กับ Numbers และ Strings แล้วและดูเหมือนว่าจะทำงานได้ดีสำหรับฉัน

ฉันหวังว่าคำตอบนี้จะช่วยได้จริง ๆ เนื่องจากฉันต่อสู้กับปัญหานี้มาหลายครั้งและมักจะจบลงด้วยการใช้วิธีแก้ปัญหา


1
ฉันเช่นนี้เพราะตรรกะอาศัยอยู่ในคอลเลกชัน แต่คุณสามารถตั้งค่าได้ง่ายsortKeyและreverseSortDirectionและโทร.sort()จากมุมมองใด ๆ ที่มีการจัดการในคอลเลกชัน ง่ายกว่าคำตอบอื่น ๆ ที่ได้รับการโหวตสูงกว่าสำหรับฉัน
Noah Heldman

5

สิ่งนี้สามารถทำได้อย่างหรูหราโดยการแทนที่เมธอด sortBy นี่คือตัวอย่าง

var SortedCollection = Backbone.Collection.extend({

   initialize: function () {
       // Default sort field and direction
       this.sortField = "name";
       this.sortDirection = "ASC";
   },

   setSortField: function (field, direction) {
       this.sortField = field;
       this.sortDirection = direction;
   },

   comparator: function (m) {
       return m.get(this.sortField);
   },

   // Overriding sortBy (copied from underscore and just swapping left and right for reverse sort)
   sortBy: function (iterator, context) {
       var obj = this.models,
           direction = this.sortDirection;

       return _.pluck(_.map(obj, function (value, index, list) {
           return {
               value: value,
               index: index,
               criteria: iterator.call(context, value, index, list)
           };
       }).sort(function (left, right) {
           // swap a and b for reverse sort
           var a = direction === "ASC" ? left.criteria : right.criteria,
               b = direction === "ASC" ? right.criteria : left.criteria;

           if (a !== b) {
               if (a > b || a === void 0) return 1;
               if (a < b || b === void 0) return -1;
           }
           return left.index < right.index ? -1 : 1;
       }), 'value');
   }

});

คุณสามารถใช้มันได้ดังนี้:

var collection = new SortedCollection([
  { name: "Ida", age: 26 },
  { name: "Tim", age: 5 },
  { name: "Rob", age: 55 }
]);

//sort by "age" asc
collection.setSortField("age", "ASC");
collection.sort();

//sort by "age" desc
collection.setSortField("age", "DESC");
collection.sort();

โซลูชันนี้ไม่ได้ขึ้นอยู่กับประเภทฟิลด์


1
สิ่งที่ดี ! "โมฆะ 0" สามารถแทนที่ด้วย "ไม่ได้กำหนด"
GMO

3
// For numbers, dates, and other number-like things
var things = new Backbone.Collection;
things.comparator = function (a, b) { return b - a };

// For strings
things.comparator = function (a, b) {
  if (a === b) return 0;
  return a < b ? -1 : 1;
};

2

นี่เป็นวิธีง่ายๆสำหรับผู้ที่ต้องการพลิกคำสั่งซื้อปัจจุบัน จะมีประโยชน์หากคุณมีตารางที่สามารถเรียงลำดับคอลัมน์ได้สองทิศทาง

collection.models = collection.models.reverse()

คุณสามารถรวมเข้ากับสิ่งนี้ได้หากคุณสนใจในตาราง (กาแฟ):

  collection.comparator = (model) ->
    model.get(sortByType)

  collection.sort()

  if reverseOrder
    collection.models = collection.models.reverse()

2
ในกรณีนี้เมื่อมีการเพิ่มโมเดลใหม่ในคอลเล็กชันโมเดลจะไม่เรียงลำดับอย่างถูกต้องเนื่องจากเมธอด. set ทำให้โมเดลที่เพิ่มใหม่อยู่ในลำดับโดยใช้เมธอด sort รับผลลัพธ์ก่อนที่จะใช้ reverseOrder
Fabio

1

สำหรับลำดับย้อนกลับใน id:

comparator: function(object) { return (this.length - object.id) + 1; }

0

ฉันมีข้อกำหนดที่แตกต่างออกไปเล็กน้อยในการที่ฉันแสดงโมเดลของฉันในตารางและฉันต้องการให้สามารถจัดเรียงตามคอลัมน์ใด ๆ (คุณสมบัติของโมเดล) และจากน้อยไปมากหรือมากไปหาน้อย

ต้องใช้เวลาพอสมควรในการทำให้ทุกกรณีทำงานได้ (โดยที่ค่าอาจเป็นสตริงสตริงว่างวันที่ตัวเลขอย่างไรก็ตามฉันคิดว่านี่ค่อนข้างใกล้เคียง

    inverseString: function(str)
    {
        return str.split("").map(function (letter) { return String.fromCharCode(-(letter.charCodeAt(0))); }).join("");
    }
    getComparisonValue: function (val, desc)
    {
        var v = 0;
        // Is a string
        if (typeof val === "string")
        {
            // Is an empty string, upgrade to a space to avoid strange ordering
            if(val.length === 0)
            {
                val = " ";
                return desc ? this.inverseString(val) : val;
            }                

            // Is a (string) representing a number
            v = Number(val);
            if (!isNaN(v))
            {
                return desc ? -1 * v : v;
            }
            // Is a (string) representing a date
            v = Date.parse(val);
            if (!isNaN(v))
            {
                return desc ? -1 * v : v;
            }
            // Is just a string
            return desc ? this.inverseString(val) : val;
        }
        // Not a string
        else
        {
            return desc ? -1 * val : val;
        }
    },

ใช้:

    comparator: function (item)
    {
        // Store the collumn to 'sortby' in my global model
        var property = app.collections.global.get("sortby");

        // Store the direction of the sorting also in the global model
        var desc = app.collections.global.get("direction") === "DESC";

        // perform a comparison based on property & direction
        return this.getComparisonValue(item.get(property), desc);
    },

0

ฉันเพิ่งลบล้างฟังก์ชันการจัดเรียงเพื่อย้อนกลับอาร์เรย์โมเดลเมื่อเสร็จสิ้น นอกจากนี้ฉันยังระงับการทริกเกอร์เหตุการณ์ 'sort' จนกว่าจะเสร็จสิ้นการย้อนกลับ

    sort : function(options){

        options = options || {};

        Backbone.Collection.prototype.sort.call(this, {silent:true});
        this.models.reverse();

        if (!options.silent){
            this.trigger('sort', this, options);
        }

        return this;
    },

0

พบปัญหานี้เมื่อเร็ว ๆ นี้และเพิ่งตัดสินใจเพิ่มวิธีการ rsort

    Collection = Backbone.Collection.extend({
            comparator:function(a, b){
                return a>b;
            },
            rsort:function(){
                var comparator = this.comparator;

                this.comparator = function(){
                    return -comparator.apply(this, arguments);
                }
                this.sort();

                this.comparator = comparator;

            }
    });

หวังว่าจะมีคนพบว่าสิ่งนี้มีประโยชน์


0

นี่เป็นวิธีที่ง่ายและรวดเร็วในการจัดเรียงแบบจำลองของคอลเลคชันโดยใช้ UnderscoreJS

 var reverseCollection = _.sortBy(this.models, function(model) {
   return self.indexOf(model) * -1;
 });

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.