使用backbone.js倒序排序字符串

40

我正在尝试对一个Backbone.js集合进行反向排序。之前有回答了如何针对整数进行此操作,但没有提到字符串。

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

chapters.comparator = function(chapter) {
  return chapter.get("title");
};

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

上面的代码将章节从A到Z进行排序,但我该如何编写一个比较器,以便将其从Z到A进行排序?


示例来自文档:http://documentcloud.github.com/backbone/#Collection-comparator - 在比较器内使用"title"而不是"page"。 - Emil Stenström
7
不是重复问题,此问题需要对字符串进行排序,而不是像其他问题建议的那样使用减号进行排序。请注意不要更改原始意思。 - František Žiačik
1
这里有一个解决方案,可以反转任何类型的sortBy比较器的方向:https://dev59.com/YG445IYBdhLWcg3wJ298#12220415 - Andrew Newdigate
6个回答

45

你可以:

  • 获取字符串中每个字符的字符编码,
  • 0xffffstring.charCodeAt的最大返回值)减去每个值,
  • 使用String.fromCharCode将其转换回反转字符的字符串

这就是你的排序键。

chapters.comparator = function(chapter) {
    return String.fromCharCode.apply(String,
        _.map(chapter.get("title").split(""), function (c) {
            return 0xffff - c.charCodeAt();
        })
    );
}

然后,瞧:

> console.log(chapters.pluck('title'));
["The Middle", "The End", "The Beginning"]

注意:如果您要比较的字符串很长(例如65 kb或更多),则可能会遇到问题(请参见下面Matt的评论)。为避免此类情况,并稍微加快比较速度,只需使用您要比较的字符串的较短片段即可。 (在上面的示例中,您可以选择chapter.get("title").slice(0, 100).split("")。)所需的切片长度将取决于您的应用程序。


1
您的比较函数现在可以作为传递给sort()的参数,无需进行任何hackery操作。请参见https://github.com/documentcloud/backbone/issues/488。 - reconbot
1
@Filip,我遇到了一个问题,当使用非常长的数组调用fromCharCode.apply时,它会锁定Safari。长度大于65536的数组将导致其完全失败并抛出RangeError。也许可以通过从比较字符串中提取前n个字符的切片来使此比较器函数更安全(并且可能更有效)。 - Matthew
这种方法并不适用于所有情况。当排序以下字符串时:1)'2010-2013',2)'2011-2013',3)'2006-2010',升序应该是3 1 2,降序应该是2 1 3。但实际上你得到的结果是升序1 2 3,降序1 2 3。 - flyfisher1952
1
在执行此操作之前,您应将字符串转换为相同的大小写。 - JD Isaacks
由于性能问题,如果您有数千条记录,可能会被降级。 - Dexygen
1
请注意,这种方法并不总是正确的。例如,foofoobar的顺序没有被颠倒。可以通过在字符串末尾附加String.fromCharCode(0xffff)来解决这个问题。 - Maximilian Hils

20
有两个版本的比较函数可供使用,一个是 sortBy 版本(在示例中已经展示),它只需要一个参数;另一个是 sort 版本,你可以返回一个更标准的排序函数,根据 文档 的说明:
“sortBy”比较器函数接收一个模型,并返回一个数字或字符串值,用于确定该模型相对于其他模型的顺序。“sort”比较器函数接收两个模型,如果第一个模型应该排在第二个模型之前,则返回-1;如果它们排名相同,则返回0;如果第一个模型应该排在第二个模型之后,则返回1。
因此,在这种情况下,我们可以编写一个不同的比较器函数:
var Chapter  = Backbone.Model;
var chapters = new Backbone.Collection;

chapters.comparator = function(chapterA, chapterB) {
  if (chapterA.get('title') > chapterB.get('title')) return -1; // before
  if (chapterB.get('title') > chapterA.get('title')) return 1; // after
  return 0; // equal
};

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

那么您应该得到如下响应:

"The Middle", "The End", "The Beginning"

1
我创建了一个变体,它可以处理数字和字符串。可以在这里找到:https://dev59.com/YG445IYBdhLWcg3wJ298 - Fasani
很好的想法,制作一个不那么具体的版本。 - Dan Smart

12
如果你正在处理非数字值,那么没有明显的方法来进行逆序排序。Backbone使用Underscore的_.sortBy()_.sortedIndex() 方法基于比较器对模型进行排序,这些方法自动按升序排序。最朴素的方法是使用chapters.pluck('title').reverse(),因为pluck的结果将是一个数组。但是,在一些集合方法上调用reverse会就地反转集合模型,所以下次再调用它时,模型将回到升序状态。你可以像这样做:
var results = [],
    titles  = chapters.pluck('title');

for(var i=0, len=titles.length; i<len; i++) {
  results.push(titles[i]);
}

results.reverse();

这不会影响你Backbone集合中的models数组,因为它会在内存中创建一个全新的results数组,但保留对原始models的引用,因此调用像save这样的方法仍将更新集合状态。

但这并不是非常优雅的解决方案,在项目中每次想要反转结果时都需要编写大量额外的代码。我认为我们可以做得更好。

为了使其正常工作,您需要在比较器方法中执行一些笨拙的JavaScript操作 - 请注意,此处未经过测试:

chapters.comparator = function(chapter) {
  var alphabet = '0123456789abcdefghijklmnopqrstuvwxyz',
      title = chapter.get('title').toLowerCase(),
      inverse_title = '',
      index;

  for(var i=0, len=title.length; i<len; i++) {
    index = alphabet.indexOf(title.charAt(i));

    if(index === -1) {
      inverse_title += title.charAt(i);
      continue;
    }

    inverse_title += alphabet.charAt(alphabet.length - index - 1);
  }

  return inverse_title;
};

这个概念可能需要改进以考虑符号等因素,但本质上它会将比较字符串反转,使得“Z”变成“0”,“Y”变成“1”等等,这应该可以产生您想要的反向排序。


12

由于Backbone仅使用.sortBy方法,因此只需代理自己的逻辑:

collectionInQuestion.sortBy = function () {
  var models = _.sortBy(this.models, this.comparator);
  if (forSomeReason) {
    models.reverse();
  }
  return models;
};

..或者将其添加到其他位置..

TweakedCollection = Backbone.Collection.extend({ sortBy: [...] })

您可以使用此比较器将集合保持按相反顺序排序:https://dev59.com/YG445IYBdhLWcg3wJ298 - Fasani

0

我刚解决了一个类似于表格排序的问题,我想分享这段代码,因为在这些回答中我没有找到太多帮助:

events: {

    'click th.sortable': function(e) {
        var $this = $(e.target),
            order = $this.hasClass('asc') ? 'desc' : 'asc',
            field = $this.data('field'); /* this is a string */

        $this.siblings().addBack().removeClass('asc desc');
        $this.addClass( order );

        this.bodyView.collection.comparator = field;
        this.bodyView.collection.sort();
        if ( order === 'desc' ) this.bodyView.collection.models.reverse();

        this.bodyView.render();
    }

},

在这种情况下,我只是将比较器设置为字符串而不是函数;该字符串必须是您要按其排序的属性名称。 如果顺序需要反转,那么我只需对模型调用reverse即可。

-4

chapter.get 前面加上减号即可。

chapters.comparator = function(chapter) {
    return -chapter.get("title");
};      

2
这不适用于字符串,而这篇文章的重点正是关于字符串的。 - Emil Stenström

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接