Tulun
Tulun

Reputation: 469

Sorting an array of objects by multiple criteria with null values

Alright, so I have an array of objects that includes null values for a certain property.

The object looks roughly like this for sorting purposes... (40 elements, but this will suffice...).

It needs to be sorted based on roulette descending (with roulette sometimes being null), then novelty, then popularity.

My head is getting a bit crushed.

This works to sort the roulette in descending, but how do I need to extend it to include the other two criteria?

Object:

[
 {
  title: 'one',
  popularity: 4,
  novelty: 3
 },
 {
  title: 'two',
  popularity: 1
  novelty: 4
 },
 {
  title: 'three',
  popularity: 5,
  novelty: 3,
  roulette: 0
 },
 {
  title: 'four',
  popularity: 5,
  novelty: 3,
  roulette: 1
 }
]

Partially working function:

  object.sort(function(a, b) {
    if (a['roulette'] == null) return 1
    if (b['roulette'] == null) return -1
    if (a['roulette'] === b['roulette']) return 0
    return b.roulette > a.roulette ? 1 : -1
  });

Upvotes: 2

Views: 1950

Answers (5)

kukkuz
kukkuz

Reputation: 42352

Here is the preferential sorting (descending) with roulette, novelty and popularity (in that order)

This handles both null and undefined - check out the demo below:

var object=[{title:"one",popularity:4,novelty:3},{title:"two",popularity:1,novelty:4},{title:"three",popularity:5,novelty:3,roulette:0},{title:"four",popularity:5,novelty:3,roulette:1},{title:"five",roulette:4,novelty:null},{title:"six",popuplarity:7},{title:"seven",novelty:8,roulette:null},{title:"eight",novelty:0},{title:"nine",popularity:10}];

function ifNumber(num) {
  if(num == undefined || num == null)
    return -Infinity;
  else
    return num;
}

var result = object.sort(function(a, b) {
  return (ifNumber(b.roulette) - ifNumber(a.roulette)) 
      || (ifNumber(b.novelty) - ifNumber(a.novelty))
      || (ifNumber(b.popuplarity) - ifNumber(a.popuplarity));
});
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386560

An attempt with sorting with priority and groups.

var data = [{ title: 'one', popularity: 4, novelty: 3 }, { title: 'two', popularity: 1, novelty: 4 }, { title: 'three', popularity: 5, novelty: 3, roulette: 0 }, { title: 'four', popularity: 5, novelty: 3, roulette: 1 }, { title: 'five', popularity: 5, novelty: 4, roulette: null }, { title: 'six', popularity: 5, novelty: 5, roulette: undefined }];

data.sort(function (a, b) {
    return (
        (a.roulette === undefined || a.roulette === null) - (b.roulette === undefined || b.roulette === null) ||
        a.roulette - b.roulette ||
        a.novelty - b.novelty ||
        a.popularity - b.popularity
    );       
});

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

Upvotes: 3

Sikorski
Sikorski

Reputation: 2691

You just have to keep on breaking ties if one parameter is same.

 obj.sort(function(a, b) {
    var rouletteDiff = compare(a.roulette, b.roulette);
    if(rouletteDiff != 0) return rouletteDiff;
    var noveltyDiff = compare(a.novelty, b.novelty);
    if(noveltyDiff != 0) return noveltyDiff;
    return compare(a.popularity, b.popularity);
  });

  function compare(x,y){
    if(x == undefined) return 1;
    if(y == undefined) return -1;
    if(x === y){
        return 0;
    }else{
        return x > y ? -1 : 1
    }
  }

Upvotes: 0

Oriol
Oriol

Reputation: 288100

Just include more conditionals:

var data = [{"title":"one","popularity":4,"novelty":3},{"title":"two","popularity":1,"novelty":4},{"title":"three","popularity":5,"novelty":3,"roulette":0},{"title":"four","popularity":5,"novelty":3,"roulette":1}];
data.sort(function(a,b) {
  if (a.roulette < b.roulette || a.roulette == null) return +1;
  if (a.roulette > b.roulette || b.roulette == null) return -1;
  if (a.novelty < b.novelty || a.novelty == null) return +1;
  if (a.novelty > b.novelty || b.novelty == null) return -1;
  if (a.popularity < b.popularity || a.popularity == null) return +1;
  if (a.popularity > b.popularity || b.popularity == null) return -1;
  return 0;
})
console.log(data);

Upvotes: 0

Rajesh
Rajesh

Reputation: 24915

You can try sorting based on weighted rank:

var data=[{title:"one",popularity:4,novelty:3},{title:"two",popularity:1,novelty:4},{title:"three",popularity:5,novelty:3,roulette:0},{title:"four",popularity:5,novelty:3,roulette:1}];

data.sort(function(a, b) {
  var r1 = a.roulette === undefined ? -1 : a.roulette;
  var r2 = b.roulette === undefined ? -1 : b.roulette;
  var n1 = a.novelty === undefined ? -1 : a.novelty;
  var n2 = b.novelty === undefined ? -1 : b.novelty;
  var p1 = a.popularity === undefined ? -1 : a.popularity;
  var p2 = b.popularity === undefined ? -1 : b.popularity;

  var r_rank = r1 > r2 ? -100 : r1 < r2 ? 100 : 0;
  var n_rank = n1 > n2 ? -10 : n1 < n2 ? 10 : 0;
  var p_rank = p1 > p2 ? -1 : p1 < p2 ? 1 : 0;

  return r_rank + n_rank + p_rank;
})

var r_rank = r1 > r2 ? -100 : r1 < r2 ? 100 : 0;
var n_rank = n1 > n2 ? -10 : n1 < n2 ? 10 : 0;
var p_rank = p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
return r_rank + n_rank + p_rank;
})

console.log(data)

Upvotes: 0

Related Questions