对一组类别和子类别进行排序

时间:2019-01-02 02:40:23

标签: javascript arrays sorting

假设我们有一个对象数组,其中每个对象代表一个类别或子类别,如下所示:

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的值。
  • 对于具有 child-category 类的对象,我们分配(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进行了限制),我可以在每个类别下正确地进行排序。谢谢!

5 个答案:

答案 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年)。

  •   
  • 如果compareFunction(a,b)大于0,则将b排序为小于a的索引(即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;
    }, [])
  ;
}