Lodash: Sort an array of objects ordering alphabets followed by a number

I have an array of objects, say:

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

Knowing that I can sort it using Lodash sortBy :

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

which will result in the following:

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

But my desired result is:

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

Please, help.

+7
javascript string sorting arrays lodash
source share
5 answers

Using Array#sort , you can apply this logic:

 // 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 a number move it down (isNaN(a.name) ? -1 : 1); 

Without 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 the lodash chain:

 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> 
+6
source share

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

When sorting, not only the first character is taken into account, but also the entire line.

 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 a given input, this prints

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

Solution with two _.sortBy()

One for pre-sorting alphabets, then another for sorting elements.

In my opinion, this is more readable, and it will not affect performance.

 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> 
+1
source share

If name is an integer prefix with z to compare.

 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
source share

Split the input, sort separately and join back:

the 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' } ] 
+1
source share

All Articles