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



我已经看到了许多有关排序的帖子,甚至在多个列上进行排序。他们似乎都对列数及其名称的数量(排序类型和方向(有亲密的了解。

我需要:

  • 具有通用的类别例程,并且在称为
  • 之前不知道任何列名或其他排序标准。
  • 它需要同时在标准中跨所有列进行分类。

标准可能看起来如下:

[
  {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'}
]

那么,如何构建一个可以使用此类标准并在所有标准键上进行排序的顺序程序?这意味着重复的员工将被整理在一起,并在其中重复的项目代码分类,并在其中重复日期。

我已经整理了一个相当简单的函数,该功能返回了范围的功能,您可以将您传递给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的答案所述,reduce()迭代了整个criteria数组,而some()在第一个非零值下停止迭代,但result || fn(...)至少在每个ITERIANION,每个ITERIANINIC中的比较命令,因此,没有评估不必要的标准比较。

如果您有一个常见的排序标准,则可以为您的排序方法具有可重复使用的功能:

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

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

然后采用Array#some并将排序结果用作返回值。此方法迭代直到数组结束或返回值是真实的,这是我们需要作为排序回调的返回值的值。在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; }

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

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

最新更新