Home > Software engineering >  Array sort fails - objects without property gets put on top
Array sort fails - objects without property gets put on top

Time:01-18

I have an object array looking like this:

let plans = [
{
    surf: 5,
    price: 299,
    cprice: 199,
    cdur: 3,
},
{
    surf: 5,
    price: 249,
    cprice: 199,
    cdur: 3,
},
{
    surf: 15,
    price: 149,
    cprice: "",
    cdur: "",
},
];

And to sort the array I'm using a comparison looking like this:

    function cpriceDesc( a, b ) {
    if ( a.cprice < b.cprice ){
    return -1;
    }
    if ( a.cprice > b.cprice ){
    return 1;
    }
    return 0;
    }

    function cpriceAsc( a, b ) {
    if ( a.cprice > b.cprice ){
    return -1;
    }
    if ( a.cprice < b.cprice ){
    return 1;
    }
    return 0;
    }

The sorting when a cprice is defined works properly but the object without a cprice value is always first in the array after sort. How can I put them last?

CodePudding user response:

You can use Number.isFinite to distinguish normal numbers from anything else, like a string.

Note that your current sort-callback functions could be simplified to just a subtraction, like return a.cprice - b.cprice. To add the above discrimination to it, you can subtract like this Number.isFinite(b.cprice) - Number.isFinite(a.cprice) and when that is 0 (they are both of the same "category") continue with the a.cprice - b.cprice subtraction (or its inverse):

const plans = [{surf: 5,price: 299,cprice: 199,cdur: 3,},{surf: 5,price: 249,cprice: 199,cdur: 3,},{surf: 15,price: 149,cprice: "",cdur: "",},];

const sortAsc = (a, b) => Number.isFinite(b.cprice) - Number.isFinite(a.cprice) || a.cprice - b.cprice;
const sortDesc = (a, b) => Number.isFinite(b.cprice) - Number.isFinite(a.cprice) || b.cprice - a.cprice;

console.log(plans.sort(sortDesc));

I would not advise to define your prices as strings when there is no price. Instead just don't have that property at all. If you would do that, the above code will still work as desired.

CodePudding user response:

You could check for the empty string and move this values to bottom, otherwise sort by the value.

let
    plans = [{ surf: 5, price: 14, cprice: "", cdur: "" }, { surf: 5, price: 299, cprice: 198, cdur: 3 }, { surf: 5, price: 249, cprice: 199, cdur: 3 }, { surf: 15, price: 149, cprice: "", cdur: "" }];
    

plans.sort((a, b) =>
    (a.cprice === '') - (b.cprice === '') ||
    a.cprice - b.cprice
);

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

CodePudding user response:

First of all I advise you not to mix the types. It is logical that cprice is of type int, if cprice is not defined then use null or undefined instead of an empty string "".

You can modify your comparison functions to check for the presence of a value in the cprice property before comparing. If one of the objects doesn't have a value for cprice (or the value is an empty string ""), you can set that object to be "greater" (or "lesser" depending on the sorting order) so that it will be placed last in the sorted array. You can modified your code like this :

function cpriceDesc(a, b) {
        if (a.cprice === "") {
            return 1;
        }
        if (b.cprice === "") {
            return -1;
        }
        if (a.cprice < b.cprice ){
            return -1;
        }
        if (a.cprice > b.cprice ){
            return 1;
        }
        return 0;
    }

    function cpriceAsc( a, b ) {
        if (a.cprice === "") {
            return 1;
        }
        if (b.cprice === "") {
            return -1;
        }
        if (a.cprice > b.cprice ){
            return -1;
        }
        if (a.cprice < b.cprice ){
            return 1;
        }
        return 0;
    }

Know that in javascript, there are much faster ways to do this kind of sorting. The answer of @trincot is surely shorter, more efficient, more readable and more maintainable.

  • Related