Search code examples
javascriptsortingjavascript-objects

Javascript dynamic sorting array of object include nulls while sorting


I have array of objects. It can contain string and Integer. I want to sort it based on property ,

In one of the sorting order(asc/desc) nulls should come first.

When property is not present or null in the array it should consider it as null. Like in some of the elements age is not defined or last name is missing

example of the array is this

function dynamicsort(property,order) {
    var sort_order = 1;
    if(order === "desc"){
        sort_order = -1;
    }
    return function (a, b){
        // a should come before b in the sorted order
        if(a[property] < b[property]){
                return -1 * sort_order;
        // a should come after b in the sorted order
        }else if(a[property] > b[property]){
                return 1 * sort_order;
        // a and b are the same
        }else{
                return 0 * sort_order;
        }
    }
}
let employees = [
    {
        firstName: 'John',
        age: 27,
        joinedDate: 'December 15, 2017'
    },

    {
        firstName: 'Ana',
        lastName: 'Rosy',
        age: 25,
        joinedDate: 'January 15, 2019'
    },

    {
        firstName: 'Zion',
        lastName: 'Albert',
        age: 30,
        joinedDate: 'February 15, 2011'
    },
    {
        firstName: 'ben',
        lastName: 'Doe',
        joinedDate: 'December 15, 2017'
    },
    {
        firstName: 'Tom',
        lastName: 'Doe',
        joinedDate: 'December 15, 2017'
    },
];

console.log("Object to be sorted");
console.log(employees);
console.log("Sorting based on the age property")
console.log(employees.sort(dynamicsort("age","desc")));
console.log("Sorting based on the age property")
console.log(employees.sort(dynamicsort("age","asc")));

console.log("Sorting based on the lastName property")
console.log(employees.sort(dynamicsort("lastName","desc")));
console.log("Sorting based on the lastName property")
console.log(employees.sort(dynamicsort("lastName","asc")));


Solution

  • One simple way would be to check whether one of the two elements is undefined before comparing them.

    You can play with the return values of the first two comparisons, and eventually remove the multiplication by sort_order and decide if you want missing fields always at the top or always at the bottom.

    function dynamicsort(property,order) {
        var sort_order = 1;
        if(order === "desc"){
            sort_order = -1;
        }
        return function (a, b){
            //check if one of the property is undefined
            if(a[property] == null){
                    return 1 * sort_order;
            }
            if(b[property] == null){
                    return -1 * sort_order;
            }
            // if we are working with strings, we can perform case-insensitive comparison
            if((typeof a[property] === 'string') && (typeof b[property] === 'string')){
                return sort_order * a[property].localeCompare(b[property], 'en', {'sensitivity': 'base'});
            }
            // a should come before b in the sorted order
            if(a[property] < b[property]){
                    return -1 * sort_order;
            // a should come after b in the sorted order
            }else if(a[property] > b[property]){
                    return 1 * sort_order;
            // a and b are the same
            }else{
                    return 0 * sort_order;
            }
        }
    }
    let employees = [
        {
            firstName: 'John',
            age: 27,
            joinedDate: 'December 15, 2017'
        },
    
        {
            firstName: 'Ana',
            lastName: 'Rosy',
            age: 25,
            joinedDate: 'January 15, 2019'
        },
    
        {
            firstName: 'Zion',
            lastName: 'Albert',
            age: 30,
            joinedDate: 'February 15, 2011'
        },
        {
            firstName: 'ben',
            lastName: 'Doe',
            joinedDate: 'December 15, 2017'
        },
        {
            firstName: 'Tom',
            lastName: 'Doe',
            joinedDate: 'December 15, 2017'
        },
    ];
    
    console.log("Object to be sorted");
    console.log(employees);
    console.log("Sorting based on the age property")
    console.log(employees.sort(dynamicsort("age","desc")));
    console.log("Sorting based on the age property")
    console.log(employees.sort(dynamicsort("age","asc")));
    
    console.log("Sorting based on the lastName property")
    console.log(employees.sort(dynamicsort("lastName","desc")));
    console.log("Sorting based on the lastName property")
    console.log(employees.sort(dynamicsort("lastName","asc")));

    Also, if you like one-line conditionals like me, and you don't care about the sorting being not stable (i.e swapping items with same property), you can write the return function as:

    /* does not perform case-insensitive comparison on strings */
    return sort_order * (a[property] == null ? 1 : (b[property] == null ? -1 : (a[property] < b[property] ? -1 : 1)))
    

    Though it might be a bit less readable😅