1

While researching on the mechanism for sorting arrays in JavaScript by multiple properties/fields/attributes, I've come across this article. For my personal project I have utilized the solution offered by @chriskelly.

function comparator(attrs) {
    return function (v1, v2) {
        return attrs
            .map(function (fld) {
                var ad = 1;
                if (fld[0] === '-') {
                   ad = -1;
                   fld = fld.substring(1);
                }
                if (v1[fld] > v2[fld]) return ad;
                if (v1[fld] < v2[fld]) return -(ad);
                return 0;
            })
            .reduce(function nonZero (v3, v4) {
                return v3 ? v3 : v4;
            }, 0);
    };
}

My input array is the following:

[{"name":"Pass9884881","fld1d":10,"fld2d":25,"fld4d":383,"fld3d":626490,"fld5a":0,"date":"09/27/2021","time":"18:23:31"},
 {"name":"Driver1","fld1d":"10","fld2d":25,"fld4d":356,"fld3d":559650,"fld5a":0,"date":"10/18/2021","time":"14:48:17"},
 {"name":"Driver321","fld1d":10,"fld2d":22,"fld4d":346,"fld3d":554400,"fld5a":1,"date":"08/08/2021","time":"22:35:03"},
 {"name":"Driverxyz","fld1d":9,"fld2d":25,"fld4d":350,"fld3d":497190,"fld5a":0,"date":"07/26/2021","time":"14:23:33"},
 {"name":"Pass1974761","fld1d":"9","fld2d":25,"fld4d":316,"fld3d":477290,"fld5a":0,"date":"10/11/2021","time":"19:20:33"},
 {"name":"Pass7374147","fld1d":"9","fld2d":23,"fld4d":279,"fld3d":376750,"fld5a":0,"date":"10/10/2021","time":"20:13:15"},
 {"name":"Driver0","fld1d":8,"fld2d":25,"fld4d":286,"fld3d":435940,"fld5a":0,"date":"07/26/2021","time":"12:31:42"},
 {"name":"Pass1536735","fld1d":"8","fld2d":25,"fld4d":236,"fld3d":329880,"fld5a":0,"date":"10/09/2021","time":"17:19:14"},
 {"name":"#xxxxyyyy","fld1d":"10","fld2d":25,"fld4d":329,"fld3d":500440,"fld5a":0,"date":"11/04/2021","time":"00:04:17"},
 {"name":"Pass7668209","fld1d":"8","fld2d":24,"fld4d":203,"fld3d":267150,"fld5a":0,"date":"10/09/2021","time":"00:22:18"}]

It meant to be sorted by fld1d (desc), fld2d (desc), fld3d (desc), fld4d (desc), fld5a (asc), date (asc) and time (asc). When the object with "name":"#xxxxyyyy" is placed the last or next to the last in the input data (gets read in from an external file in the code), the resulting sorted array appears incorrect: Incorrectly sorted array

However, if I move that object in the input file to the 8th position (out of 10) or above, the resulting sorted array is correct: Correctly sorted array

I ought to admit, I am not a professional developer - just an interested party who somewhat learnt to follow examples, and I'm having hard time explaining the discrepancy in the results.

Suggestions would be greatly appreciated.

pilchard
  • 12,414
  • 5
  • 11
  • 23
bigbikefan
  • 13
  • 3
  • Provide a minimal, reproducible example if you want to improve your chances of getting this right. – Domi Nov 04 '21 at 17:25
  • Are you asking for how I used the comparator function? If so, I loaded the aforementioned data to a myArray variable from an input file and executed the function as myArray.sort(comparator('-fld1d', '-fld2d', '-fld3d', '-fld4d', 'fld5a', 'date', 'time')) - the same manner as described in that article I referred to in the question. – bigbikefan Nov 07 '21 at 02:16

1 Answers1

1

You could take a simplified approach and chain all sorting crieria by taking the delta or use a string comparison.

const
    getISO = (date, time) => `${date.slice(3, 5)}-${date.slice(0, 2)}-${date.slice(6, 10)}T${time}`;
    data = [{ name: "Pass9884881", fld1d: 10, fld2d: 25, fld4d: 383, fld3d: 626490, fld5a: 0, date: "09/27/2021", time: "18:23:31" }, { name: "Driver1", fld1d: "10", fld2d: 25, fld4d: 356, fld3d: 559650, fld5a: 0, date: "10/18/2021", time: "14:48:17" }, { name: "Driver321", fld1d: 10, fld2d: 22, fld4d: 346, fld3d: 554400, fld5a: 1, date: "08/08/2021", time: "22:35:03" }, { name: "Driverxyz", fld1d: 9, fld2d: 25, fld4d: 350, fld3d: 497190, fld5a: 0, date: "07/26/2021", time: "14:23:33" }, { name: "Pass1974761", fld1d: "9", fld2d: 25, fld4d: 316, fld3d: 477290, fld5a: 0, date: "10/11/2021", time: "19:20:33" }, { name: "Pass7374147", fld1d: "9", fld2d: 23, fld4d: 279, fld3d: 376750, fld5a: 0, date: "10/10/2021", time: "20:13:15" }, { name: "Driver0", fld1d: 8, fld2d: 25, fld4d: 286, fld3d: 435940, fld5a: 0, date: "07/26/2021", time: "12:31:42" }, { name: "Pass1536735", fld1d: "8", fld2d: 25, fld4d: 236, fld3d: 329880, fld5a: 0, date: "10/09/2021", time: "17:19:14" }, { name: "#xxxxyyyy", fld1d: "10", fld2d: 25, fld4d: 329, fld3d: 500440, fld5a: 0, date: "11/04/2021", time: "00:04:17" }, { name: "Pass7668209", fld1d: "8", fld2d: 24, fld4d: 203, fld3d: 267150, fld5a: 0, date: "10/09/2021", time: "00:22:18" }],

data.sort((a, b) =>
    b.fld1d - a.fld1d ||
    b.fld2d - a.fld2d ||
    b.fld3d - a.fld3d ||
    b.fld4d - a.fld4d ||
    a.fld5a - b.fld5a ||
    getISO(a.date, a.time).localeCompare(getISO(b.date, b.time))
);

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • I am confused with a.fld5d - b.fld5d piece of code not blowing up with an error under "Run code snippet", as there is no fld5d field, but there is fld5a in the data. – bigbikefan Nov 04 '21 at 17:58
  • it works because of unknown property is `undefined` and by sustraction the result is `NaN` which forces (by being *falsy*) the last expression to evaluate. – Nina Scholz Nov 04 '21 at 18:02
  • Thank you for clarifying and for the suggestion. The used syntax is next to impossible to understand for me, but your solution does seem to work regardless of the initial order of objects in the input data. I was hoping to learn why the code in the question is sensitive to the input data order. – bigbikefan Nov 04 '21 at 19:09