使用Backbone.js反向排序


89

使用Backbone.js,我可以使用比较器功能来设置集合。很好地对模型进行了排序,但是我想颠倒顺序。

如何对模型进行降序排序而不是升序排序?


2
有关字符串(包括created_at字段)的反向排序,请参见另一个问题的答案
Eric Hu

2
没有为某种风格比较支持,因为2012年初刚刚接受2个参数和返回-1,0或1。github.com/documentcloud/backbone/commit/...

这是对我工作(字符串和数字):stackoverflow.com/questions/5013819/...
FMD

Answers:


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用于使用与mine!相同的示例代码,且相差15秒以内。我必须说如初衷。
2011年

哦,当然,只有减号。谢谢你们的快速回复!
德鲁·达拉·阿布拉姆斯

还要注意的是(如Backbone的网站所述),如果您更改模型的任何属性(例如,在章中添加“长度”属性),则会中断此操作。在这种情况下,您将必须在集合上手动调用“ sort()”。
cmcculloh 2012年

9
没有为某种风格比较支持,因为2012年初刚刚接受2个参数和返回-1,0或1。github.com/documentcloud/backbone/commit/...

@Fasani响应适用于数字以外的类型,请看看:stackoverflow.com/questions/5013819/...
JayC

56

就个人而言,我对此处提供的任何解决方案都不满意:

  • 当排序类型为非数字时,乘以-1解决方案将无法正常工作。尽管有一些解决方法(通过致电Date.getTime()例如),但这些情况是特定的。我想用一种通用的方法来反转任何排序的方向,而不必担心要排序的字段的特定类型。

  • 对于字符串解决方案,一次构造一个字符的字符串似乎是性能瓶颈,尤其是在使用大型集合(或者实际上是大型排序字段字符串)时

这是一个适用于String,Date和Numeric字段的解决方案:

首先,声明一个比较器,它将反转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
Andrew Newdigate

非常好,我最后做了一些调整,并将其抽象到Backbone.Collection的原型中。谢谢一群!
肯德里克·莱德

46

Backbone.js的集合比较器依赖于Underscore.js方法_.sortBy。实现sortBy的方式最终以“包装” javascript .sort()的方式使反向排序字符串变得困难。字符串的简单否定最终返回NaN并破坏了排序。

如果您需要对字符串执行反向排序(例如反向字母排序),则这是一种非常骇人听闻的方法:

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

它绝非漂亮,但它是一个“字符串否定器”。如果您对在javascript中修改本机对象类型没有任何限制,则可以通过提取字符串否定符并将其添加为String.Prototype的方法来使代码更清晰。但是,您可能只想知道自己在做什么就可以这样做,因为在javascript中修改本机对象类型可能会产生无法预料的后果。


这非常方便。谢谢安德鲁!
亚伯

1
没问题。我只想补充一点,您应该问自己,为什么要实施反向字母排序,以及从用户体验角度看是否存在更优雅的解决方案。
安德鲁·德·安德拉德

1
@AndrewDeAndrade原因:我想按'created_at'属性对集合进行排序,首先提供最新的集合。此属性是Backbone的默认属性之一,并存储为String。您的答案最接近我想要的。谢谢!
Eric Hu

令人惊奇的是,过去一个小时来一直在努力解决此问题。

28

我的解决方案是在排序后反转结果。

为防止双重渲染,请先使用无声排序,然后触发“重置”。

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

2
更好的解决方案是否定比较器的结果。
Daniel X Moore

7
Daniel,这并非总是可能的,例如在对字符串或日期进行排序时。
加文·舒尔茨

1
当然是啦。对于Date,您可以让比较器返回-Date.getTime(),如果您相信系统中的日期将跨越unix时代,则可能会有一些额外的技巧。对于字母,请查看上面的安德鲁的答案。
2012年

@DanielXMoore为什么那一定是更好的解决方案?它更快吗?就个人而言,我发现存储用户上次对集合排序的属性非常容易,如果用户再次对该属性进行排序,则反转当前排序;(要实现您在Wikipedia表中看到的排序行为,可以通过重复单击表头中的属性来切换排序方向)这样,如果我们从Asc切换到其他代码,则可以保持比较器逻辑整洁并为自己保存排序操作来描述排序顺序
Mansiemans 2013年

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

},

比较器除外的两个模型,在更改集合或模型时,compartor函数将运行并更改集合的顺序。

我已经用数字和字符串测试了这种方法,它似乎对我来说是完美的。

我真的希望这个答案可以为我提供帮助,因为我已经为这个问题苦苦挣扎了很多次,并且通常以一种变通的方式结束工作。


1
我喜欢这样,因为逻辑住在集合中,但是你可以很容易地设置sortKeyreverseSortDirection和呼叫.sort()从对收集处理任何看法。对我来说,比其他投票更高的答案更简单。
诺亚·赫德曼

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
好一个!“ void 0”可以替换为“ 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

对于那些只想翻转当前订单的人来说,这是一个非常简单的解决方案。如果您有一个表的列可以在两个方向上排序,这将很有用。

collection.models = collection.models.reverse()

如果您对表案例(咖啡脚本)感兴趣,可以将其与类似的内容结合使用:

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

  collection.sort()

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

2
在这种情况下,将新模型添加到集合中时,由于.set方法使用sort方法保持新添加的模型的顺序,因此无法正确排序,请在应用reverseOrder之前获取结果。
法比奥

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.