1

How can I sort an object array which has null and undefined values using javascript. My target is to show items that has property of "jobTitle" first, order them by rating, then items with no "jobTitle" ordered by rating again. Data:

data = [
  {name: 'John', rating: null},
  {name: 'Ethel', rating: 1.34, jobTitle: 'engineer'},
  {name: 'Abba', rating: 5.44},
  {name: 'Harry', rating: 0.44, jobTitle: 'plumber'}
]

After data is sorted by jobTitle and then by rating it should be like:

[
  {name: 'Ethel', rating: 1.34, jobTitle: 'engineer'},
  {name: 'Harry', rating: 0.44, jobTitle: 'plumber'},
  {name: 'Abba', rating: 5.44},
  {name: 'John', rating: null}
]

I have tried many variations like:

data.sort(function (a, b) {
  var x = a[key]; var y = b[key];
  return ((x > y) ? -1 : ((x < y) ? 1 : 0));
});

but it did not work for undefined and null values. I do not want to use low quality solutions like creating new arrays, fill with data and merge them. I want to achieve that in single method if possible.

EDIT

In general response should show first those who has jobTitle orderd by rating AND then those who don't have jobTitle ALSO ordered by rating.

IntoTheDeep
  • 4,027
  • 15
  • 39
  • 84
  • firstly, that's not JSON - so ... should undefined (which won't ever appear in JSON) and null be treated as high or low? oh, I see, high, right – Jaromanda X May 01 '20 at 07:29
  • @JaromandaX this is JSON. Null should be at the bottom, so I guess low – IntoTheDeep May 01 '20 at 07:32
  • this is NOT JSON ... `data =` ... means it's just an array of objects - you may have retrieved it from some API as JSON, but it's been parsed and is no longer JSON – Jaromanda X May 01 '20 at 07:35
  • @IntoTheDeep JSON and JS native objects are not the same, [Here](https://stackoverflow.com/questions/8294088/javascript-object-vs-json) you can read more about it. – AZ_ May 01 '20 at 08:02
  • @JaromandaX seems correct to me – IntoTheDeep May 01 '20 at 08:35
  • @IntoTheDeep - it only looks that way due to chance - it won't work properly because I sorted incorrectly based on other persons incorrect input – Jaromanda X May 01 '20 at 10:29

3 Answers3

4

You can check for each condition, and return the response to the callback in expected manner.

data = [
  {name: 'John', rating: null},
  {name: 'Ethel', rating: 1.34, jobTitle: 'engineer'},
  {name: 'Abba', rating: 5.44},
  {name: 'Harry', rating: 0.44, jobTitle: 'plumber'}
];


data.sort((a, b) => {
  if(a.jobTitle && b.jobTitle) {
    return (b.rating || 0) - (a.rating || 0);
  } else if(a.jobTitle || b.jobTitle) {
    return !(a.jobTitle) - !(b.jobTitle)
  } else  {
    return (b.rating || 0) - (a.rating || 0);
  }
});
console.log(data)
AZ_
  • 3,094
  • 1
  • 9
  • 19
  • sorry @AZ_ - you're right, read my response to you in my question - some meddling person critiqued my first answer to tell me "sort by job title first" ... whereas it's not that at all - I've removed my comment, and if the OP could just un-accept my answer, I'll delete the abomination :p – Jaromanda X May 01 '20 at 08:14
  • and I was scratching my head :D – AZ_ May 01 '20 at 08:15
  • @AZ_ I have accepted this answer. Out of scope: if I want to show those with jobTitle after those who haven't whant must I change? THank you – IntoTheDeep May 01 '20 at 08:51
  • 1
    @IntoTheDeep you can simply change the `!` to `!!` i.e. `!!(a.jobTitle) - !!(b.jobTitle)` or just switch the `a and b` i.e. `return !(b.jobTitle) - !(a.jobTitle)` – AZ_ May 01 '20 at 08:58
0

I would recommend splitting your code up in 2 sorts and use them in sequence.

let result = [
  {name: 'John', rating: null},
  {name: 'Peter', rating: null},
  {name: 'Harry', rating: 0.44, jobTitle: 'plumber'},
  {name: 'Harry', rating: 0.50, jobTitle: 'plumber'},
  {name: 'Anderson', rating: 1.34, jobTitle: 'mascot'},
  {name: 'Ethel', rating: 1.34, jobTitle: 'engineer'},
  {name: 'Abba', rating: 5.44}
]
result.sort(by_rating);
result.sort(by_job_title);
console.log(result)

function by_job_title(a, b ){
  if( typeof a.jobTitle === 'undefined' && typeof a.jobTitle === 'undefined' ) return 0;
  if( typeof b.jobTitle === 'undefined' ) return -1;
  if( typeof a.jobTitle === 'undefined' ) return 1;
  
  let title_a = a.jobTitle.toLowerCase();
  let title_b = b.jobTitle.toLowerCase();
  
  if( title_a === title_b ) return 0;
  if( title_a > title_b ) return 1;
  else return -1;
}

function by_rating( a, b ) {
  // notice that this works with null, only because null will be casted to 0
  if( a.rating === b.rating ) return 0;
  if( a.rating > b.rating ) return -1;
  else return 1;
}
Rob Monhemius
  • 4,822
  • 2
  • 17
  • 49
-3

In your sort function you can check if a value is null or undefined and return 1 in that case, so it's sorted to the end of the array. As a sidenote: You're sorting an array, not a JSON string. A JSON string can not contain the undefined primitive value, while it is valid Javascript.

data.sort(function (a, b) {
  var x = a[key];
  var y = b[key];
  if (x === null || x === undefined) {
    return 1;
  }
  return y - x;
});
Luuuud
  • 4,206
  • 2
  • 25
  • 34