0

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")));

1
  • 1
    I have updated my answer, see below Commented May 29, 2021 at 22:32

3 Answers 3

2

You could check in advance and move undefined or nullvalues to top.

function dynamicsort(property, order) {
    const
        isNullOrUndefined = v => v === undefined || v === null,
        sort_order = -(order === "desc") || 1;

    return function(a, b) {
        return sort_order * (
            isNullOrUndefined(a[property]) - isNullOrUndefined(b[property])||
            (a[property] > b[property]) - (a[property] < b[property])
        );
    }
}

const
    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', age: null }];

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

console.log("Sorting based on the lastName property desc")
console.log(employees.sort(dynamicsort("lastName", "desc")));
console.log("Sorting based on the lastName property asc")
console.log(employees.sort(dynamicsort("lastName", "asc")));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

3 Comments

In both asc and desc nulls are at the top. i need them it only once at the top. Thanks for the help. Really appreciate...
what order do you want instead?
In descending order nulls should come first. and in asc nulls should be at the bottom
1

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😅

4 Comments

Thanks alot!! This answer works for me because in only one sort I needed nulls to be at the top...
No problem, I edited the answer to check for null-ness and not just undefined, since checking == null "includes" checking for undefined
observed one issue with this. String with small case letter are sorted at the bottom or top. Cant we treat small case and large same?
it is indeed possible, I'll edit my answer
1

I will do that this way: (with the help of Nullish coalescing operator (??) )

little extra: this sort automatically supports the data types: numeric, string, and string representing a date.

(for boolean values : true = 1 and false = 0)

function dynamicsort( property, order='asc' )
  {
  let sortOrder = (order === 'desc') ? -1 : 1
 
  return function(a_Obj,b_Obj)
    {
    let a = a_Obj[property] ?? null
      , b = b_Obj[property] ?? null
      ;
    if (a===null || b===null) return (a===b)? 0 : (a===null)? -1 : +1 
    if (!isNaN(a))            return (a - b) * sortOrder 
    if (isNaN(Date.parse(a))) return (a.localeCompare(b)) * sortOrder 
    return (Date.parse(a) - Date.parse(b)) * sortOrder 
    }
  }

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

let verif = ''  

verif = 'sort -> firstName asc\n'
employees
  .sort(dynamicsort('firstName','asc'))
  .forEach(o=> verif += JSON.stringify(o) + '\n' )
console.log( verif )

verif = 'sort -> firstName desc\n'
employees
  .sort(dynamicsort('firstName','desc'))
  .forEach(o=> verif += JSON.stringify(o) + '\n' )
console.log( verif )

verif = 'sort -> lastName asc\n'
employees
  .sort(dynamicsort('lastName','asc'))
  .forEach(o=> verif += JSON.stringify(o) + '\n' )
console.log( verif )

verif = 'sort -> lastName desc\n'
employees
  .sort(dynamicsort('lastName','desc'))
  .forEach(o=> verif += JSON.stringify(o) + '\n' )
console.log( verif )

verif = 'sort -> age asc\n'
employees
  .sort(dynamicsort('age','asc'))
  .forEach(o=> verif += JSON.stringify(o) + '\n' )
console.log( verif )

verif = 'sort -> age desc\n'
employees
  .sort(dynamicsort('age','desc'))
  .forEach(o=> verif += JSON.stringify(o) + '\n' )
console.log( verif )


verif = 'sort -> joinedDate asc\n'
employees
  .sort(dynamicsort('joinedDate','asc'))
  .forEach(o=> verif += JSON.stringify(o) + '\n' )
console.log( verif )

verif = 'sort -> joinedDate desc\n'
employees
  .sort(dynamicsort('joinedDate','desc'))
  .forEach(o=> verif += JSON.stringify(o) + '\n' )
console.log( verif )
.as-console-wrapper {max-height: 100%!important;top:0}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.