Is it possible to pass a custom comparator to lodash's sortBy function?
Solution 1
You can use lodash mixin's
_.mixin({
sortWith : function(arr, customFn) {
return _.map(arr).sort(customFn)
}
});
You can now do
_.sortWith(array, function(a, b) {
//custom function that returns either -1, 0, or 1 if a is <, ==, or > than b
});
You can now chain this like:
_.chain(myObject)
.get('some_array_property')
.sortWith(function(a, b) {
//determine if a <=> b
})
.value();
Internally, sortWith maps the array to a new array so that it doesn't modify the array passed into it and uses the native sort() method.
Solution 2
No, unfortunately this is not currently possible.
A workaround is to use the iteratees
function to map the values to something the standard comparator will sort correctly. This is however almost never practical.
It's also asked for here https://github.com/lodash/lodash/issues/246, but no response from the author.
Solution 3
As said by other answers, you cannot pass in a comparator to _.sortBy
like in Array.prototype.sort()
.
One workaround would be to add a new calculated property to the objects which would be the ordering and then use _.sortBy
on that.
So if you had a list of objects like [{name: "hello there"}, {name: "world"}]
but you wanted to sort them by name length, you could do:
_(arr)
//augment each object with a calculated `order` property
.map(obj => ({...obj, order: obj.name.length}))
.sortBy('order')
.value()
Result: [{name: "world", order: 5}, {name: "hello there", order: 11}]
Solution 4
This is enough for simple ordering based on finite values.
const MAP = {
BRONZE: 1,
SILVER: 2,
GOLD: 3,
PLATINUM: 4,
}
const DATA = [
{ name: 'A', type: 'SILVER' },
{ name: 'B', type: 'BRONZE' },
{ name: 'C', type: 'PLATINUM' },
{ name: 'F', type: 'SILVER' },
{ name: 'G', type: 'GOLD' },
{ name: 'H', type: 'BRONZE' },
]
_.sortBy(DATA, (item) => MAP[item.type])
Result:
[
{"name":"B","type":"BRONZE"},
{"name":"H","type":"BRONZE"},
{"name":"A","type":"SILVER"},
{"name":"F","type":"SILVER"},
{"name":"G","type":"GOLD"},
{"name":"C","type":"PLATINUM"}
]
Solution 5
actually iteratees can be mapping for the return result:
const users = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 36 },
{ 'user': 'fred', 'age': 40 },
{ 'user': 'barney', 'age': 34 }
];
_.sortBy(users, [function(o) { return o.user; }]);
// output: objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
or iteratees can be normal js sort function like i made in the below example to sort array of objects to sort cards when cardsStatus === 'NORI'
so card should be on top of array
const cardsBeforeSort = [
{
"cardStatus": "NORM",
"consumedLimit": 0,
"cardAccountSerial": "10551880",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": -9,
"key": "405433******8106"
},
{
"cardStatus": "NORI",
"consumedLimit": 0,
"cardAccountSerial": "10551908",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": 1,
"key": "405433******8382"
},
{
"cardStatus": "HOLD",
"consumedLimit": -169122.81,
"cardAccountSerial": "10548192",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdjj",
"nickName": "",
"aan": "123",
"balance": 5579.29,
"key": "417323******3321"
},
{
"cardStatus": "NORI",
"consumedLimit": -7.74,
"cardAccountSerial": "10549814",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": 1,
"key": "429927******1548"
}
]
const sortedCards = sortBy(userCards, [
(first, second) =>
first.cardStatus === 'NORI' ? -1 : second === 'NORI' ? 1 : 0,
]);
this will result in the following output:
console.log(sortedCards);
[
{
"cardStatus": "NORI",
"consumedLimit": -7.74,
"cardAccountSerial": "10549814",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": 1,
"key": "429927******1548"
},
{
"cardStatus": "NORI",
"consumedLimit": 0,
"cardAccountSerial": "10551908",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": 1,
"key": "405433******8382"
},
{
"cardStatus": "NORM",
"consumedLimit": 0,
"cardAccountSerial": "10551880",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": -9,
"key": "405433******8106"
},
{
"cardStatus": "HOLD",
"consumedLimit": -169122.81,
"cardAccountSerial": "10548192",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdjj",
"nickName": "",
"aan": "123",
"balance": 5579.29,
"key": "417323******3321"
},
]
actually the benefit of using sortBy lodash function is being functional programming immutable solution because of not mutating cardsBeforeSort array
Related videos on Youtube
Daniel
Updated on October 31, 2022Comments
-
Daniel over 1 year
For example, I want to sort with respect to
Intl.Collator().compare
. Is there any way to pass this comparator to be used by_.sortBy
?-
J. Titus about 7 yearsWhat are the items that are being sorted?
-
Mike Cluck about 7 yearsIs there a reason you are not using the native
sort
method? -
Daniel about 7 yearsThe items that are being sorted are objects with a name string field that may contain foreign language characters. --- Currently I am using the native
sort
doing something likearr.slice.sort((a, b) => Intl.Collator().compare(a.name, b.name))
, but I was wondering if there was a cleaner way.
-
-
Gianmarco over 3 yearsis this a new feature? was this added in recent times? Because
sortBy
is already mentioned in the question but without the feature you used. If so please specify in the answer from which version oflodash
this is available to complete your answer. -
undeadborn over 3 yearsLooks like it was already in earlier versions. Lodash 1.3.1