在javascript中同时对多个列进行排序,包括不同的方向和排序类型

时间:2018-02-07 20:19:19

标签: javascript arrays sorting

我看过很多关于排序的帖子,甚至还有一些关于多列排序的帖子。他们似乎都对列的数量及其名称,排序类型和方向有着深入的了解。

我需要:

  • 有一个通用的排序例程,在调用之前不知道任何列名或其他排序条件。
  • 需要同时对条件中的所有列进行排序。

标准可能如下所示:

[
  {key: 'Employee', type: 'alpha', dir: 'asc'}, 
  {key: 'ProjectCode', type: 'numeric', dir: 'desc'}, 
  {key: 'WorkDate', type: 'date', dir: 'desc'}
]

测试数据:

data = [
    {Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/5/18'},  
    {Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/7/18'},  
    {Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/5/18'},
    {Employee: 'John Doe', ProjectCode: 4574, WorkDate: '1/5/18'},  
    {Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/6/18'},  
    {Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/7/18'}
];

结果数据:

[
    {Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/7/18'},
    {Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/5/18'},  
    {Employee: 'John Doe', ProjectCode: 4574, WorkDate: '1/5/18'},  
    {Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/7/18'},  
    {Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/6/18'},  
    {Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/5/18'}
]

那么如何构建一个可以使用这些条件的排序例程,并一次对所有条件键进行排序?意味着重复的员工将被排序在一起,并在其中重复项目代码一起排序,并在其中重复日期。

3 个答案:

答案 0 :(得分:1)

我已经整理了一个相当简单的函数,它返回一个可以传递给Array.prototype.sort()的范围函数:

function sortBy (criteria) {
  const sign = { asc: 1, desc: -1 };
  const sort = {
    numeric: (a, b) => a - b,
    date: (a, b) => new Date(a) - new Date(b),
    alpha: (a, b) => a.localeCompare(b)
  };
  
  const compare = criteria.map(
    ({ key, type, dir }) => (a, b) => sign[dir] * sort[type](a[key], b[key])
  );

  return (a, b) => compare.reduce((result, fn) => result || fn(a, b), 0);
}

let criteria = [
  {key: 'Employee', type: 'alpha', dir: 'asc'}, 
  {key: 'ProjectCode', type: 'numeric', dir: 'desc'}, 
  {key: 'WorkDate', type: 'date', dir: 'desc'}
];
let data = [
  {Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/5/18'},  
  {Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/7/18'},  
  {Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/5/18'},
  {Employee: 'John Doe', ProjectCode: 4574, WorkDate: '1/5/18'},  
  {Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/6/18'},  
  {Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/7/18'}
];

console.log(data.sort(sortBy(criteria)));

注意:作为Nina's answer statesreduce()会迭代整个criteria数组,而some()会在第一个非零值处停止迭代,但是result || fn(...)至少在每次迭代中使比较调用短路,因此没有不必要的标准比较被评估。

如果您有一个通用的排序标准,您还可以为您的排序方法提供可重复使用的功能:

const sortByMyCriteria = sortBy(criteria);

arrayA.sort(sortByMyCriteria);
arrayB.sort(sortByMyCriteria);
...

ES5重写sortBy()方法:

function sortBy (criteria) {
  var sign = { asc: 1, desc: -1 };
  var sort = {
    numeric: function (a, b) { return a - b; },
    date: function (a, b) { return new Date(a) - new Date(b); },
    alpha: function (a, b) { return a.localeCompare(b); }
  };

  var compare = criteria.map(function (c) {
    var key = c.key, type = c.type, dir = c.dir;

    return function (a, b) {
      return sign[dir] * sort[type](a[key], b[key]);
    };
  });

  return function (a, b) {
    return compare.reduce(function (result, fn) {
      return result || fn(a, b);
    }, 0);
  };
}

答案 1 :(得分:1)

您可以定义一些简单的函数来比较两个值,并将它们存储在一个对象中,以便通过sortCriteria对象的type属性进行访问。

然后取Array#some并使用排序结果作为返回值。此方法迭代直到数组结束或返回值为truthy,这是我们需要的值作为排序回调的返回值。在some回调内部,方向与函数一起使用,以使用函数的两个参数重新调整相对值。

var data = [{ Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/5/18' }, { Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/7/18' }, { Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/5/18' }, { Employee: 'John Doe', ProjectCode: 4574, WorkDate: '1/5/18' }, { Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/6/18' }, { Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/7/18' }],
    sortFn = {
        alpha: function (a, b) { return a.localeCompare(b); },
        numeric: function (a, b) { return a - b; },
        date: function (a, b) { return new Date(a) - new Date(b); }
    },
    sortCriteria = [{ key: 'Employee', type: 'alpha', dir: 'asc' }, { key: 'ProjectCode', type: 'numeric', dir: 'desc' }, { key: 'WorkDate', type: 'date', dir: 'desc' }];

data.sort(function (a, b) {
    var value = 0;
    return sortCriteria.some(function (o) {
        return value = (o.dir === 'asc' || -1) * sortFn[o.type](a[o.key], b[o.key]);
    }) && value;
});

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 2 :(得分:0)

使用其他帖子中的一些想法我想出了以下代码似乎工作正常并以正确的顺序返回数组。



data = [
	{Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/5/18'},  
	{Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/7/18'},  
	{Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/5/18'},
	{Employee: 'John Doe', ProjectCode: 4574, WorkDate: '1/5/18'},  
	{Employee: 'John Doe', ProjectCode: 3443, WorkDate: '1/6/18'},  
	{Employee: 'Harry Smith', ProjectCode: 3443, WorkDate: '1/7/18'}
];
sortCriteria = [
  {key: 'Employee', type: 'alpha', dir: 'asc'}, 
  {key: 'ProjectCode', type: 'numeric', dir: 'desc'}, 
  {key: 'WorkDate', type: 'date', dir: 'desc'}
];

data.sort(function(a, b) {
	var x, y, i;
  
	for (i=0; i < sortCriteria.length; i++) {
  	x = a[sortCriteria[i].key];
    y = b[sortCriteria[i].key];
    
		if (sortCriteria[i].type === 'date') {
    	x = new Date(x).getTime();
      y = new Date(y).getTime();
    }
    if (sortCriteria[i].type === 'numeric') {
    	x = parseFloat(x);
      y = parseFloat(y);
    }
    
		if (sortCriteria[i].dir === 'asc') {
      if (x < y) return -1;
      if (x > y) return 1;
    } else {
      if (x > y) return -1;
      if (x < y) return 1;
    }
  }
  return 0;
});

console.log(data);
&#13;
&#13;
&#13;