假设我们有一个对象数组,其中每个对象代表一个类别或子类别,如下所示:
var data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2}
];
此数组代表一个具有两个类别的示例,其中每个类别还具有两个子类别。
现在,我面临着以某种自定义方式对数组进行排序的问题。如您所注意到的,每个对象内的order
属性与对象在其层次结构中的位置(或顺序)有关。类别和子类别之间的关系由属性parentCategoryId
定义。
作为示例,我需要使用以前的数据数组作为预期的排序:
var data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2}
];
我当前解决此问题的方法是基于创建数字映射,并在以下条件下分析属性的值:
categoryId * 1000
的值。(parentCategoryId * 1000) + order
此逻辑在下一个排序实现中显示:
let data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2}
];
let orderedData = data.sort((a, b) =>
{
var aCat = (a.type == "parent-category") ? a.categoryId : a.parentCategoryId;
var aOrder = (a.type == "parent-category") ? 0 : a.order;
var bCat = (b.type == "parent-category") ? b.categoryId : b.parentCategoryId;
var bOrder = (b.type == "parent-category") ? 0 : b.order;
return (aCat * 1000 + aOrder) - (bCat * 1000 + bOrder);
});
console.log(orderedData);
但是,忽略了以前的实现有效的事实,我的问题是是否存在解决此问题的更好方法或替代方法。我不喜欢依赖于数字的映射,因为,例如,先前的实现对子类别的数量(在这种情况下为999
进行了限制),我可以在每个类别下正确地进行排序。谢谢!
答案 0 :(得分:2)
如果性能不是问题,我宁愿使用更分步的方法:
/* ORIGINAL DATA */
const data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2}
];
const sorted = [];
const byOrder = (a, b) => a.order - b.order;
// Get and sort parents first
const parents = data.filter(obj => obj.type === 'parent-category');
parents.sort(byOrder);
// Push to resulting array by parent order
parents.forEach(obj => {
sorted.push(obj);
// Get and sort corresponding children objs
const children = data.filter(o => o.parentCategoryId === obj.categoryId);
children.sort(byOrder);
// Push the objs into resulting array together
sorted.push.apply(sorted, children);
});
console.log(sorted);
此方法涉及更多步骤,但与复杂的排序逻辑相比,它很简单且易于理解。
仅使用外部库的一项功能就太过分了。
答案 1 :(得分:2)
使用Array#filter Array#sort和Array#map的简单性能解决方案
var data=[{type:"parent-category",order:1,categoryId:1},{type:"parent-category",order:2,categoryId:2},{type:"child-category",order:1,categoryId:3,parentCategoryId:1},{type:"child-category",order:2,categoryId:4,parentCategoryId:1},{type:"child-category",order:2,categoryId:5,parentCategoryId:2},{type:"child-category",order:1,categoryId:6,parentCategoryId:2}]
let res = data
.filter(({type}) => type === "parent-category")
.sort((a,b) => a.order - b.order)
.reduce((acc, curr) =>{
const children = data
.filter(({parentCategoryId}) => parentCategoryId === curr.categoryId)
.sort((a,b) => a.order - b.order);
acc.push(curr, ...children);
return acc;
}, []);
console.log(res);
答案 2 :(得分:0)
为简便起见,请使用lodash#orderBy
如果您不喜欢 lodash ,则可以在sort
我还没有完全了解您的排序标准,但是,例如,
function sort_categories(cat1,cat2) {
if (cat1["order"] < cat2["order"])
return -1;
if (cat1["order"] > cat2["order"])
return 1;
return 0;
}
data.sort(sort_categories);
将仅按订单排序。
有关返回值的详细信息,请使用look at here
如果提供了compareFunction,则所有未定义的数组元素为 根据比较函数的返回值排序(全部 未定义的元素排序到数组的末尾,不调用 compareFunction)。如果a和b是要比较的两个元素,则:
如果compareFunction(a,b)小于0,则将a排序为小于b的索引(即a排在最前面)。
如果compareFunction(a,b)返回0,则使a和b相对不变,但对所有不同的a进行排序 元素。注意:ECMAscript标准不保证这一点 行为,因此并非所有浏览器(例如Mozilla版本约会 至少要追溯到2003年)。
- 当给定一对特定的元素a和b作为其两个参数时,
如果compareFunction(a,b)大于0,则将b排序为小于a的索引(即b排在最前面)。
compareFunction(a,b)必须始终返回相同的值。如果 返回不一致的结果,然后不确定排序顺序。
答案 3 :(得分:0)
这将起作用。您首先对categoryId排序,然后通过运行forEach函数进行数学运算。
var data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2}
];
data.sort( function(a, b){
return a.categoryId - b.categoryId;
}).forEach( function(itm){
itm.categoryId = itm.categoryId * 1000;
if( itm.parentCategoryId){
itm.parentCategoryId = itm.parentCategoryId * 1000 + itm.order
}
});
console.log(data);
或者以ES6方式...
let orderedData = data.sort( (a, b) => {
return a.categoryId - b.categoryId;
}).forEach( itm => {
itm.categoryId = itm.categoryId * 1000;
if( itm.parentCategoryId){
itm.parentCategoryId = itm.parentCategoryId * 1000 + itm.order
}
});
答案 4 :(得分:0)
基于@kemicofa答案。刚刚对其进行了更改以支持两个以上级别
function iterative(categories, parentCategory, level = 0) {
return categories
.filter((category) => parentCategory ? category.parent === parentCategory : !category.parent)
.map(category => {
category.level = level;
return category;
})
.sort((a,b) => a.name.localeCompare(b.name))
.reduce((acc, curr) =>{
const children = iterative(categories, curr.id, level+1)
acc.push(curr, ...children);
return acc;
}, [])
;
}