Javascript Backbone.js 集合比较器按多个字段排序?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8921667/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Backbone.js collection comparator sort by multiple fields?
提问by Harry
this.col = Backbone.Collection.extend({
model: M,
comparator: function(item) {
return item.get("level");
}
});
This above code sorts items by level. I want to sort by level, then by title. Can I do that? Thanks.
上面的代码按级别对项目进行排序。我想按级别排序,然后按标题排序。我可以这样做吗?谢谢。
回答by Bo Jeanes
@amchang87's answer definitely works, but another that I found worked is simply returning an array of the sortable fields:
@amchang87 的答案绝对有效,但我发现另一个有效的答案只是返回一个可排序字段的数组:
this.col = Backbone.Collection.extend({
model: M,
comparator: function(item) {
return [item.get("level"), item.get("title")]
}
});
I haven't tested this in multiple browsers yet as I think it relies on JS' behavior in sort order for arrays (based on their contents). It definitely works in WebKit.
我还没有在多个浏览器中测试过这个,因为我认为它依赖于 JS 对数组排序的行为(基于它们的内容)。它绝对适用于 WebKit。
回答by hyong
String concatenation works fine when sorting multiple fields in ascending order, but it didn't work for me because 1) I had to support asc/desc per field and 2) certain fields were number field (i.e., I want 10 to come after 2 if it is ascending). So, below was a comparator function I used and worked OK for my needs. It assumes the backbone collection has a variable assigned with 'sortConfig', which is an array of JSON objects with field name and sort order direction. For example,
按升序对多个字段进行排序时,字符串连接工作正常,但它对我不起作用,因为 1) 我必须支持每个字段的 asc/desc 和 2) 某些字段是数字字段(即,我希望 10 在 2 之后出现如果它是上升的)。所以,下面是我使用的比较器函数,可以满足我的需要。它假设主干集合有一个分配有“sortConfig”的变量,它是一个 JSON 对象数组,具有字段名称和排序顺序方向。例如,
{
"sort" : [
{
"field": "strField",
"order": "asc"
},
{
"field": "numField",
"order": "desc"
},
...
]
}
With the JSON object above assigned as 'sortConfig' to the collection, the function below will make Backbone sort by strField in ascending order first, then sort by numField in descending order, etc. If no sort order is specified, it sorts ascending by default.
将上面的 JSON 对象指定为 'sortConfig' 到集合中,下面的函数将使 Backbone 先按 strField 升序排序,然后按 numField 降序排序等。如果未指定排序顺序,则默认升序排序.
multiFieldComparator: function(one, another) {
// 'this' here is Backbone Collection
if (this.sortConfig) {
for (var i = 0; i < this.sortConfig.length; i++) {
if (one.get(this.sortConfig[i].field) > another.get(this.sortConfig[i].field)) {
return ("desc" != this.sortConfig[i].order) ? 1 : -1;
} else if (one.get(this.sortConfig[i].field) == another.get(this.sortConfig[i].field)) {
// do nothing but let the loop move further for next layer comparison
} else {
return ("desc" != this.sortConfig[i].order) ? -1 : 1;
}
}
}
// if we exited out of loop without prematurely returning, the 2 items being
// compared are identical in terms of sortConfig, so return 0
// Or, if it didn't get into the if block due to no 'sortConfig', return 0
// and let the original order not change.
return 0;
}
回答by Lawrence
Returning an array is not consistent if you need to sort descending and some ascending...
如果您需要按降序和升序排序,则返回数组是不一致的...
I created a small set of functions which can be used to return the relevant comparison integer back to Backbone Comparator function:
我创建了一小组函数,可用于将相关的比较整数返回给 Backbone Comparator 函数:
回答by adrian
The main thing is that Backbone sorts by a single relative value of one item to another. So it's not directly possible to sort twice in a single collection but I'd try this.
主要的是,Backbone 按一个项目与另一个项目的单个相对值进行排序。所以不能直接在一个集合中排序两次,但我会试试这个。
this.col = Backbone.Collection.extend({
model: M,
comparator: function(item) {
// make sure this returns a string!
return item.get("level") + item.get("title");
}
});
What this will do is return a string of like "1Cool", "1title", "2newTitle" ... Javascript should sort the strings by the numerical character first then each character afterwards. But this will only work as long as your levels have the same amount of digits. IE "001title" vs "200title". The main idea though is that you need to produce two comparable objects, line a number or string, that can be compared to each other based on one criteria.
这将做的是返回一个像“1Cool”、“1title”、“2newTitle”这样的字符串……Javascript 应该先按数字字符对字符串进行排序,然后再按每个字符排序。但这只有在您的级别具有相同数量的数字时才有效。IE“001title”与“200title”。但主要思想是您需要生成两个可比较的对象,一个数字或字符串,可以根据一个标准相互比较。
Other solution would be to use underscore to "groupby" your level then use "sortby" to manually sort each level group then manually replace the underlying collection with this newly created array. You can probably setup a function to do this whenever the collection "changes".
其他解决方案是使用下划线“分组”您的级别,然后使用“sortby”手动对每个级别组进行排序,然后用这个新创建的数组手动替换基础集合。您可以设置一个函数来在集合“更改”时执行此操作。
回答by useless
"inspired" in hyong answer.
“启发”在hyong回答。
This also allows you to change the data before compare it, valueTransforms is an object, if there is an attribute in that object that has a function, it will be used.
这也允许您在比较之前更改数据,valueTransforms 是一个对象,如果该对象中有一个具有功能的属性,它将被使用。
/*
* @param {Object} sortOrders ie:
* {
* "description": "asc",
* "duedate": "desc",
* }
* @param {Object} valueTransforms
*/
setMultiFieldComparator: function(sortOrders, valueTransforms) {
var newSortOrders = {}, added = 0;
_.each(sortOrders, function(sortOrder, sortField) {
if (["asc", "desc"].indexOf(sortOrder) !== -1) {
newSortOrders[sortField] = sortOrder;
added += 1;
}
});
if (added) {
this.comparator = this._multiFieldComparator
.bind(this, newSortOrders, valueTransforms || this.model.prototype.valueTransforms || {});
} else {
this.comparator = null;
}
},
_multiFieldComparator: function(sortOrders, valueTransforms, one, another) {
var retVal = 0;
if (sortOrders) {
_.every(sortOrders, function(sortOrder, sortField) {
var oneValue = one.get(sortField),
anotherValue = another.get(sortField);
if (valueTransforms[sortField] instanceof Function) {
oneValue = valueTransforms[sortField](oneValue);
anotherValue = valueTransforms[sortField](anotherValue);
}
if (oneValue > anotherValue) {
retVal = ("desc" !== sortOrder) ? 1 : -1;
} else if (oneValue < anotherValue) {
retVal = ("desc" !== sortOrder) ? -1 : 1;
} else {
//continue
return true;
}
});
}
return retVal;
},