Đảo ngược thứ tự sắp xếp với Backbone.js


89

Với Backbone.js, tôi đã thiết lập một bộ sưu tập với hàm so sánh. Nó sắp xếp các mô hình một cách độc đáo, nhưng tôi muốn đảo ngược thứ tự.

Làm cách nào để sắp xếp các mô hình theo thứ tự giảm dần thay vì tăng dần?


2
Đối với phân loại ngược trên Strings (bao gồm các lĩnh vực created_at), xem câu trả lời này trên một câu hỏi khác
Eric Hồ

2
Có hỗ trợ cho bộ so sánh loại kiểu kể từ đầu năm 2012. Chỉ chấp nhận 2 đối số và trả lại -1, 0 hoặc 1. github.com/documentcloud/backbone/commit/...
Geon

Đây là những gì đã làm việc cho tôi (Chuỗi và Số): stackoverflow.com/questions/5013819/…
FMD

Câu trả lời:


133

Vâng, bạn có thể trả về giá trị âm từ trình so sánh. Ví dụ, nếu chúng ta lấy ví dụ từ trang web của Backbone và muốn đảo ngược thứ tự, nó sẽ như thế này:

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 vì sử dụng mã ví dụ giống như mã của tôi !, và chênh lệch trong vòng 15 giây. Tôi phải nói giống như tâm trí.
DhruvPathak

Ồ, tất nhiên, chỉ là một dấu trừ. Cảm ơn cả hai vì phản hồi nhanh chóng của bạn!
Drew Dara-Abrams

Cũng lưu ý rằng (như đã đề cập trên trang của Backbone), điều này sẽ phá vỡ nếu bạn thay đổi bất kỳ thuộc tính nào của mô hình (ví dụ: nếu bạn thêm thuộc tính "length" vào chương). Trong trường hợp đó, bạn sẽ phải gọi thủ công "sort ()" trên Bộ sưu tập.
cmcculloh

9
Có hỗ trợ cho bộ so sánh loại kiểu kể từ đầu năm 2012. Chỉ chấp nhận 2 đối số và trả lại -1, 0 hoặc 1. github.com/documentcloud/backbone/commit/...
Geon

Phản hồi @Fasani hoạt động cho các loại khác với số, vui lòng xem: stackoverflow.com/questions/5013819/…
JayC

56

Cá nhân tôi không hài lòng với bất kỳ giải pháp nào được đưa ra ở đây:

  • Giải pháp nhân với -1 không hoạt động khi kiểu sắp xếp không phải là số. Mặc dù có những cách giải quyết vấn đề này (bằng cách gọi Date.getTime()ví dụ) những trường hợp này là cụ thể. Tôi muốn một cách tổng quát để đảo ngược hướng của bất kỳ loại nào mà không phải lo lắng về loại trường cụ thể được sắp xếp.

  • Đối với giải pháp chuỗi, việc xây dựng một chuỗi một ký tự tại một thời điểm có vẻ giống như một nút cổ chai về hiệu suất, đặc biệt là khi sử dụng các bộ sưu tập lớn (hoặc thực sự, các chuỗi trường sắp xếp lớn)

Đây là một giải pháp hoạt động hiệu quả cho các trường Chuỗi, Ngày và Số:

Đầu tiên, khai báo một bộ so sánh sẽ đảo ngược kết quả của một kết quả của một hàm sortBy, như sau:

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;
  };
}

Bây giờ, nếu bạn muốn đảo ngược hướng của loại, tất cả những gì chúng ta cần làm là:

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'));

Lý do điều này hoạt động là Backbone.Collection.sorthoạt động khác nhau nếu hàm sắp xếp có hai đối số. Trong trường hợp này, nó hoạt động theo cách giống như trình so sánh được truyền vào Array.sort. Giải pháp này hoạt động bằng cách điều chỉnh hàm sortBy đối số đơn thành một bộ so sánh hai đối số và đảo ngược kết quả.


7
Là người có câu trả lời được ủng hộ nhiều nhất cho câu hỏi này, tôi thực sự thích cách tiếp cận này và nghĩ rằng nó sẽ có nhiều lượt ủng hộ hơn.
Andrew De Andrade

Cảm ơn bạn rất nhiều Andrew
Andrew Newdigate

Rất tuyệt, tôi đã chỉnh sửa điều này một chút và trừu tượng hóa nó trong nguyên mẫu của Backbone.Collection. Cảm ơn nhiều!
Kendrick Ledet

46

Bộ so sánh bộ sưu tập của Backbone.js dựa vào phương thức Underscore.js _.sortBy. Cách sortBy được triển khai kết thúc bằng cách "gói" javascript .sort () theo cách làm cho việc sắp xếp ngược lại các chuỗi trở nên khó khăn. Sự phủ định đơn giản của chuỗi kết thúc trả về NaN và phá vỡ sắp xếp.

Nếu bạn cần thực hiện sắp xếp ngược lại với Chuỗi, chẳng hạn như sắp xếp theo thứ tự bảng chữ cái ngược, đây là một cách thực sự khó thực hiện:

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;
}

Nó không có nghĩa là đẹp, nhưng nó là một "bộ phủ định chuỗi". Nếu bạn không gặp bất kỳ e ngại nào với việc sửa đổi các loại đối tượng gốc trong javascript, bạn có thể làm cho mã của bạn rõ ràng hơn bằng cách trích xuất trình phủ định chuỗi và thêm nó như một phương thức trên String.Prototype. Tuy nhiên, bạn có thể chỉ muốn làm điều này nếu bạn biết mình đang làm gì, vì việc sửa đổi các loại đối tượng gốc trong javascript có thể gây ra những hậu quả không lường trước được.


Điều này rất tiện dụng. Cảm ơn Andrew!
Abel

1
Không thành vẫn đề. Tôi chỉ muốn nói thêm rằng bạn nên tự hỏi bản thân tại sao bạn đang triển khai sắp xếp theo thứ tự bảng chữ cái ngược và liệu có lẽ có một giải pháp thanh lịch hơn từ góc độ trải nghiệm người dùng.
Andrew De Andrade,

1
@AndrewDeAndrade Về lý do: Tôi muốn sắp xếp bộ sưu tập theo thuộc tính 'create_at', phân phát những bộ sưu tập mới nhất trước tiên. Thuộc tính này là một trong những thuộc tính mặc định của Backbone và được lưu trữ dưới dạng Chuỗi. Câu trả lời của bạn gần nhất với những gì tôi muốn. Cảm ơn!
Eric Hu

Điều này thật tuyệt vời, chúng tôi đang nghiên cứu giải pháp cho vấn đề này trong một giờ qua.

28

Giải pháp của tôi là đảo ngược kết quả sau khi sắp xếp.

Để ngăn kết xuất kép, hãy sắp xếp đầu tiên với im lặng, sau đó kích hoạt 'đặt lại'.

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

2
Một giải pháp tốt hơn là phủ định kết quả của bộ so sánh.
Daniel X Moore

7
Daniel, điều đó không phải lúc nào cũng khả thi, chẳng hạn như khi sắp xếp chuỗi hoặc Ngày.
Gavin Schulz

1
Chắc chắn là như vậy. Đối với Ngày, bạn có thể có kết quả so sánh -Date.getTime(), có thể có thêm một số hack nếu bạn tin rằng ngày trong hệ thống của mình sẽ vượt qua kỷ nguyên unix. Đối với thứ tự bảng chữ cái, hãy xem câu trả lời của Andrew ở trên.
asparagino

@DanielXMoore Tại sao đó nhất thiết phải là giải pháp tốt hơn? Nó có nhanh hơn không? Cá nhân tôi thấy rất dễ dàng để lưu trữ thuộc tính mà người dùng đã sắp xếp bộ sưu tập lần cuối, sau đó đảo ngược cách sắp xếp hiện tại nếu anh ta lại sắp xếp theo thuộc tính đó; (để triển khai hành vi sắp xếp mà bạn thấy trong bảng Wikipedia, nơi hướng sắp xếp có thể được chuyển đổi bằng cách nhấp liên tục vào thuộc tính trong tiêu đề bảng) Bằng cách đó, tôi giữ logic trình so sánh của mình sạch sẽ và tự lưu thao tác sắp xếp nếu chúng ta chuyển từ Asc để quyết định thứ tự sắp xếp
Mansiemans

13

Sửa đổi hàm so sánh của bạn để trả về một số giá trị theo tỷ lệ nghịch thay vì trả về dữ liệu hiện tại của bạn. Một số mã từ: http://documentcloud.github.com/backbone/#Collection-comparator

Thí dụ:

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

Những điều sau đây hoạt động tốt cho tôi:

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

Tôi đã đọc một số nơi mà hàm so sánh Backbone sử dụng hoặc bắt chước hàm Array.prototype.sort () của JavaScript. Điều này có nghĩa là nó sử dụng 1, -1 hoặc 0 để quyết định thứ tự sắp xếp của từng mô hình trong bộ sưu tập. Điều này liên tục cập nhật và bộ sưu tập vẫn theo thứ tự chính xác dựa trên bộ so sánh.

Thông tin về Array.prototype.sort () có thể được tìm thấy tại đây: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en-US&redirectslug=JavaScript% 2FReference% 2FGlobal_Objects% 2FArray% 2Fsort

Nhiều câu trả lời cho câu hỏi này chỉ đơn giản là đảo ngược thứ tự ngay trước khi đưa nó lên trang. Đây không phải là lý tưởng.

Sử dụng bài viết trên trên Mozilla làm hướng dẫn, tôi đã có thể sắp xếp bộ sưu tập của mình theo thứ tự ngược lại bằng cách sử dụng mã sau, sau đó chúng tôi chỉ cần hiển thị bộ sưu tập sang trang theo thứ tự hiện tại và chúng ta có thể sử dụng cờ (reverseSortDirection) để đảo ngược thứ tự .

//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;
      }

},

Bộ so sánh ngoại trừ hai mô hình, khi thay đổi bộ sưu tập hoặc mô hình, chức năng bộ so sánh sẽ chạy và thay đổi thứ tự của bộ sưu tập.

Tôi đã thử nghiệm cách tiếp cận này với Numbers và Strings và dường như nó hoạt động hoàn hảo đối với tôi.

Tôi thực sự hy vọng câu trả lời này có thể giúp ích vì tôi đã vật lộn với vấn đề này nhiều lần và thường kết thúc bằng cách sử dụng một công việc xung quanh.


1
Tôi như thế này bởi vì logic sống trong bộ sưu tập, nhưng bạn có thể dễ dàng thiết lập sortKeyreverseSortDirectionvà cuộc gọi .sort()từ bất kỳ điểm cho rằng có một tay cầm vào bộ sưu tập. Đối với tôi, đơn giản hơn những câu trả lời được bình chọn cao hơn khác.
Noah Heldman

5

Điều này có thể được thực hiện một cách trang nhã bằng cách ghi đè phương thức sortBy. Đây là một ví dụ

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');
   }

});

Vì vậy, bạn có thể sử dụng nó như thế này:

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();

Giải pháp này không phụ thuộc vào loại trường.


1
Tốt! "void 0" có thể được thay thế bằng "undefined".
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

Đây là một giải pháp thực sự đơn giản, dành cho những người chỉ đơn giản muốn lật thứ tự hiện tại. Sẽ rất hữu ích nếu bạn có một bảng có các cột có thể được sắp xếp theo hai hướng.

collection.models = collection.models.reverse()

Bạn có thể kết hợp nó với một cái gì đó như thế này nếu bạn quan tâm đến trường hợp bảng (coffeescript):

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

  collection.sort()

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

2
Trong trường hợp này, khi một mô hình mới được thêm vào bộ sưu tập, nó sẽ không được sắp xếp chính xác vì phương thức .set giữ cho mô hình mới được thêm vào theo thứ tự bằng cách sử dụng phương pháp sắp xếp, lấy kết quả từ trước khi áp dụng reverseOrder.
Fabio

1

Đối với thứ tự ngược lại trên id:

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

0

Tôi có một yêu cầu hơi khác, đó là tôi đang hiển thị các mô hình của mình trong một bảng và tôi muốn có thể sắp xếp chúng theo bất kỳ cột nào (thuộc tính mô hình) và tăng dần hoặc giảm dần.

Phải mất rất nhiều thời gian để làm cho tất cả các trường hợp hoạt động (trong đó các giá trị có thể là chuỗi, chuỗi rỗng, ngày tháng, số. Tuy nhiên, tôi nghĩ điều này khá gần.

    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;
        }
    },

sử dụng:

    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

Tôi chỉ ghi đè hàm sắp xếp để đảo ngược mảng mô hình khi hoàn thành. Ngoài ra, tôi đã tạm dừng việc kích hoạt sự kiện 'sắp xếp' cho đến khi hoàn thành việc đảo ngược.

    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

Gần đây đã gặp phải vấn đề này và chỉ quyết định thêm một phương thức 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;

            }
    });

Hy vọng rằng ai đó sẽ thấy điều này hữu ích


0

Đây là một cách nhanh chóng và dễ dàng để sắp xếp ngược lại các mô hình của bộ sưu tập bằng cách sử dụng UnderscoreJS

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.