Lodash:排序对象数组,首先排列字母,然后是数字

时间:2022-02-19 15:57:48

I have an array of objects, let's say:

我有一个对象数组,让我们说:

var objects = [
  {name: 'A'},
  {name: '1'},
  {name: 'B'}
]

Knowing that I can sort it using Lodash sortBy:

知道我可以使用Lodash sortBy对它进行排序:

objects= _.sortBy(objects, 'name')

which will result in this:

这将导致:

[
  {name: '1'},
  {name: 'A'},
  {name: 'B'}
]

But my desired output is this:

但我想要的输出是这样的:

[
  {name: 'A'},
  {name: 'B'},
  {name: '1'}
]

Please help.

请帮忙。

5 个解决方案

#1


6  

Using Array#sort you can apply this logic:

使用Array#sort您可以应用此逻辑:

// If both are numbers or both are not numbers
isNaN(a.name) === isNaN(b.name) ?
     // then compare between them 
    a.name.localeCompare(b.name)
    : // else
    // If the 1st is not a number move it up, if it's a number move it down
    (isNaN(a.name) ? -1 : 1); 

Without lodash:

没有lodash:

var objects = [{"name":"A"},{"name":"3"},{"name":"1"},{"name":"B"}];

objects.sort(function(a, b) {
  return isNaN(a.name) === isNaN(b.name) ? a.name.localeCompare(b.name) : (isNaN(a.name) ? -1 : 1);
});

console.log(objects);

As part of a lodash's chain:

作为lodash链的一部分:

var objects = [{"name":"A"},{"name":"3"},{"name":"1"},{"name":"B"}];

var result = _(objects)
  .sort(function(a, b) {
    return isNaN(a.name) === isNaN(b.name) ? a.name.localeCompare(b.name) : (isNaN(a.name) ? -1 : 1);
  }) 
  .value();

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.3/lodash.min.js"></script>

#2


2  

I'm not sure if using lodash's sortBy is the correct approach for this problem. Here's an implementation using Javascript's Array#sort method.

我不确定使用lodash的sortBy是否是解决此问题的正确方法。这是使用Javascript的Array#排序方法的实现。

It takes not only the first character but the entire string into account when doing the sorting.

在进行排序时,不仅要考虑第一个字符,还要考虑整个字符串。

const objects = [{
  name: '2'
}, {
  name: 'B'
}, {
  name: '1'
}, {
  name: 'A'
}, {
  name: 'A1'
}, {
  name: 'AA'
}]

objects.sort((o1, o2) => {
  let a = o1.name, b = o2.name;
  let isDigit = x => x >= 48 && x <= 57;

  for (let i = 0, n = Math.min(a.length, b.length); i < n; i++) {
    let aCharCode = a.charCodeAt(i), bCharCode = b.charCodeAt(i);
    
    if (aCharCode !== bCharCode) {
      if (isDigit(aCharCode) && !isDigit(bCharCode)) return 1;
      if (isDigit(bCharCode) && !isDigit(aCharCode)) return -1;
      return aCharCode - bCharCode;
    }
  }

  return a.length - b.length;
});

console.log(objects)

For the given input, this prints out

对于给定的输入,打印出来

[
  {
    "name": "A"
  },
  {
    "name": "AA"
  },
  {
    "name": "A1"
  },
  {
    "name": "B"
  },
  {
    "name": "1"
  },
  {
    "name": "2"
  }
]

#3


1  

Solution with two _.sortBy()

One for priotizing alphabets first, then another one for sorting the elements.

一个用于首先打印字母,然后另一个用于对元素进行排序。

In my opinion it's more readable and it will have no performance impact.

在我看来,它更具可读性,不会对性能产生影响。

const objects = [
  {name: 'B'},
  {name: '2'},
  {name: '1'},
  {name: 'A'}
]

const result = _.sortBy(_.sortBy(objects, o => !isNaN(parseInt(o.name)), 'name'))

console.log(result)
<script src="https://lodash.com/vendor/cdn.jsdelivr.net/lodash/4.17.3/lodash.min.js"></script>

#4


1  

Partition the input, sort separately and join back:

对输入进行分区,单独排序并加入:

Code:

码:

const _ = require('lodash');

const customSort = (a) => _.chain(_.partition(a, i => isNaN(i.name))).flatMap(p => _.sortBy(p, 'name')).value();

const input = [
    { name: '1' },
    { name: 'A' },
    { name: '6' },
    { name: 'B' },
    { name: 'a' },
    { name: '0' },
    { name: '3' }];

console.log(customSort(input));

Output:

输出:

[ { name: 'A' },
  { name: 'B' },
  { name: 'a' },
  { name: '0' },
  { name: '1' },
  { name: '3' },
  { name: '6' } ]

#5


1  

If name is an integer prefix it with z for comparison.

如果name是整数前缀,则使用z进行比较。

var objects = [
  {name: 'z'},
  {name: 'A'},
  {name: '1'},
  {name: 'B'}
], sorted = _.sortBy( objects, [
        function(d) {
            return !isNaN(parseFloat(d.name))
                   && isFinite(d.name)
                       ? 'z' + d.name
                       : d.name;
        }
    ]
);
console.log(sorted);
<script src="https://lodash.com/vendor/cdn.jsdelivr.net/lodash/4.17.3/lodash.min.js"></script>

#1


6  

Using Array#sort you can apply this logic:

使用Array#sort您可以应用此逻辑:

// If both are numbers or both are not numbers
isNaN(a.name) === isNaN(b.name) ?
     // then compare between them 
    a.name.localeCompare(b.name)
    : // else
    // If the 1st is not a number move it up, if it's a number move it down
    (isNaN(a.name) ? -1 : 1); 

Without lodash:

没有lodash:

var objects = [{"name":"A"},{"name":"3"},{"name":"1"},{"name":"B"}];

objects.sort(function(a, b) {
  return isNaN(a.name) === isNaN(b.name) ? a.name.localeCompare(b.name) : (isNaN(a.name) ? -1 : 1);
});

console.log(objects);

As part of a lodash's chain:

作为lodash链的一部分:

var objects = [{"name":"A"},{"name":"3"},{"name":"1"},{"name":"B"}];

var result = _(objects)
  .sort(function(a, b) {
    return isNaN(a.name) === isNaN(b.name) ? a.name.localeCompare(b.name) : (isNaN(a.name) ? -1 : 1);
  }) 
  .value();

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.3/lodash.min.js"></script>

#2


2  

I'm not sure if using lodash's sortBy is the correct approach for this problem. Here's an implementation using Javascript's Array#sort method.

我不确定使用lodash的sortBy是否是解决此问题的正确方法。这是使用Javascript的Array#排序方法的实现。

It takes not only the first character but the entire string into account when doing the sorting.

在进行排序时,不仅要考虑第一个字符,还要考虑整个字符串。

const objects = [{
  name: '2'
}, {
  name: 'B'
}, {
  name: '1'
}, {
  name: 'A'
}, {
  name: 'A1'
}, {
  name: 'AA'
}]

objects.sort((o1, o2) => {
  let a = o1.name, b = o2.name;
  let isDigit = x => x >= 48 && x <= 57;

  for (let i = 0, n = Math.min(a.length, b.length); i < n; i++) {
    let aCharCode = a.charCodeAt(i), bCharCode = b.charCodeAt(i);
    
    if (aCharCode !== bCharCode) {
      if (isDigit(aCharCode) && !isDigit(bCharCode)) return 1;
      if (isDigit(bCharCode) && !isDigit(aCharCode)) return -1;
      return aCharCode - bCharCode;
    }
  }

  return a.length - b.length;
});

console.log(objects)

For the given input, this prints out

对于给定的输入,打印出来

[
  {
    "name": "A"
  },
  {
    "name": "AA"
  },
  {
    "name": "A1"
  },
  {
    "name": "B"
  },
  {
    "name": "1"
  },
  {
    "name": "2"
  }
]

#3


1  

Solution with two _.sortBy()

One for priotizing alphabets first, then another one for sorting the elements.

一个用于首先打印字母,然后另一个用于对元素进行排序。

In my opinion it's more readable and it will have no performance impact.

在我看来,它更具可读性,不会对性能产生影响。

const objects = [
  {name: 'B'},
  {name: '2'},
  {name: '1'},
  {name: 'A'}
]

const result = _.sortBy(_.sortBy(objects, o => !isNaN(parseInt(o.name)), 'name'))

console.log(result)
<script src="https://lodash.com/vendor/cdn.jsdelivr.net/lodash/4.17.3/lodash.min.js"></script>

#4


1  

Partition the input, sort separately and join back:

对输入进行分区,单独排序并加入:

Code:

码:

const _ = require('lodash');

const customSort = (a) => _.chain(_.partition(a, i => isNaN(i.name))).flatMap(p => _.sortBy(p, 'name')).value();

const input = [
    { name: '1' },
    { name: 'A' },
    { name: '6' },
    { name: 'B' },
    { name: 'a' },
    { name: '0' },
    { name: '3' }];

console.log(customSort(input));

Output:

输出:

[ { name: 'A' },
  { name: 'B' },
  { name: 'a' },
  { name: '0' },
  { name: '1' },
  { name: '3' },
  { name: '6' } ]

#5


1  

If name is an integer prefix it with z for comparison.

如果name是整数前缀,则使用z进行比较。

var objects = [
  {name: 'z'},
  {name: 'A'},
  {name: '1'},
  {name: 'B'}
], sorted = _.sortBy( objects, [
        function(d) {
            return !isNaN(parseFloat(d.name))
                   && isFinite(d.name)
                       ? 'z' + d.name
                       : d.name;
        }
    ]
);
console.log(sorted);
<script src="https://lodash.com/vendor/cdn.jsdelivr.net/lodash/4.17.3/lodash.min.js"></script>