简体   繁体   中英

Is it possible to pass a custom comparator to lodash's sortBy function?

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 ?

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.

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.

Not exactly sure what you are looking for. But if you are finding ways to use comparator in lodash sort, this may help:

Using _.chain() to get lodash collect will enable you to pass in comparator to sort()

console.log(JSON.stringify(_.sortBy(res, lat))); // sortBy doesn't take in comparator

console.log(
  JSON.stringify(
    _.chain(res)
      .sort((a, b) => b.lat - a.lat) // sort takes in comparator
      .value()
  )
);

Not lodash, but might come in handy for someone looking for native sort.

 var customSort = ( selector, options, locales = undefined, ) => (a, b) => { return selector(a).localeCompare(selector(b), locales, {numeric: true,...options}); }; var x = [ { name: '1-test' }, { name: '01-test' }, { name: '11-test' }, { name: '11-Test' }, { name: '10-test' }, { name: '40-btest' }, { name: '40-ctest' }, { name: '40-atest' }, { name: '2-test' }, { name: '20-test' }, { name: 'ätest' }, { name: 'atest' }, ]; console.log(x.sort(customSort((x) => x.name))); console.log(x.sort(customSort((x) => x.name, { caseFirst: 'upper' }))); // in Swedish console.log(x.sort(customSort((x) => x.name, { sensitivity: 'base' },'sv')));

Options come from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare

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}]

Simple implementaion for lodash/fp (functional programming lodash) with currying (not restricted to lodash actually):

const sortWith = comparator => list => list.map(i => i).sort(comparator);

And for TypeScript:

type ComparatorFn<T> = (a: T, b: T) => number;

const sortWith = <P>(comparator: ComparatorFn<P>) => (list: P[]): P[] => list.map(i => i).sort(comparator);

Found the corresponding issue, here is stated that the mentioned feature is already merged long ago but unfortunately not released yet. https://github.com/lodash/lodash/pull/3764 . This would be really great to have it available

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"}
]

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM