Home > Mobile >  Following pattern for multiple sorts
Following pattern for multiple sorts

Time:03-25

Is the following pattern a good way to do multiple sorts, for example something along the lines of:

ORDER BY id ASC, name DESC, date ASC

let people = [
    {Name: 'Alex', Age: 20},
    {Name: 'George', Age: 20},
    {Name: 'Tommy', Age: 11},
];
function cmpPeople(p1, p2) {
    // Age ASC
    if (p1.Age != p2.Age) {
        return p1.Age - p2.Age; // numeric fields can use subtraction
    } 
    // Name DESC
    else if (p1.Name != p2.Name) {
        return (p1.Name > p2.Name) ? -1 : 1;
    } 
    // Placeholder at end
    else return 0;
}
people.sort(cmpPeople);
console.log(people);

If not, what might be a cleaner way to chain together multiple sorts similar to how it's communicated with SQL?

CodePudding user response:

What you have works, but here's a more succinct method perhaps. Using the arrow function, we can return a result without return. The first comparison returns the age difference unless it's zero, in which case it returns the second comparison which uses localeCompare

let people = [{Name: 'Alex', Age: 20}, {Name: 'George', Age: 20}, {Name: 'Tommy', Age: 11}];
const cmpPeople = (p1, p2) =>  p1.Age - p2.Age || p2.Name.localeCompare(p1.Name)
people.sort(cmpPeople);
console.log(people);

CodePudding user response:

You could take an array of wanted keys and their order and iterate this array until a value is different from zero.

const
    ASC = (a, b) => a > b || -(a < b),
    DESC = (a, b) => ASC(b, a),
    sortBy = columns => (a, b) => {
        let r = 0;

        columns.some(([key, fn]) => r = fn(a[key], b[key]));

        return r;
    }
    people = [{ Name: 'George', Age: 20 }, { Name: 'Alex', Age: 20 }, { Name: 'Tommy', Age: 11 }],
    order = [
        ['Age', ASC],
        ['Name', ASC]
    ];

people.sort(sortBy(order));

console.log(people);
.as-console-wrapper { max-height: 100% !important; top: 0; }

CodePudding user response:

The direction is right. The else is redundant. Also your comparator is very specific (contains business logic), you can't reuse it. I took it as an exercise and wrote generic comparator generator.

let people = [
    { Name: "Alex", Age: 20 },
    { Name: "George", Age: 20 },
    { Name: "George", Age: 11 },
    { Name: "George", Age: 10 },
    { Name: "Tommy", Age: 11 },
];

function getComparator(fields) {
    return function (p1, p2) {
        for (let i = 0; i < fields.length; i  ) {
            const v1 = p1[fields[i].field];
            const v2 = p2[fields[i].field];
            // It should work for numbers & strings in same way
            if (v1 > v2) return fields[i].order === "asc" ? 1 : -1;
            if (v1 < v2) return fields[i].order === "asc" ? -1 : 1;
        }
        return 0;
    };
}

const comparator = getComparator([
    { field: "Age", order: "asc" },
    { field: "Name", order: "desc" },
]);

people.sort(comparator);
console.log(people);

  • Related