5

I'd like to sort an array by subject (string, alphabetical), then level (int, ascending), then name (string, alphabetical). I have found numerous other threads on SO but I haven't found any which sort by multiple variable types. I thought I had it, but it isn't sorting properly.

The sort function:

scipads.sort(function (a, b) {
    if (a.subject != b.subject)
        return a.subject < b.subject;
    if (a.level != b.level)
        return a.level - b.level;
    return a.name < b.name;
});

The array:

var scipads = [{
        "name": "L2 Physics Externals",
        "level": 2,
        "subject": "physics",
    }, {
        "name": "L2 Physics Internals",
        "level": 2,
        "subject": "physics",
    }, {
        "name": "L2 Chem Externals",
        "level": 2,
        "subject": "chemistry",
    }, {
        "name": "L2 Chem Internals",
        "level": 2,
        "subject": "chemistry",
    }, {
        "name": "L2 Bio Internals",
        "level": 2,
        "subject": "biology",
    }, {
        "name": "L2 Bio Externals",
        "level": 2,
        "subject": "biology",
    }, {
        "name": "L1 Electricity & Magnetism",
        "level": 1,
        "subject": "physics",
    }, {
        "name": "L1 Wave Behaviour",
        "level": 1,
        "subject": "physics",
    }, {
        "name": "L1 Heat",
        "level": 1,
        "subject": "physics",
    }, {
        "name": "L1 Carbon Chemistry",
        "level": 1,
        "subject": "chemistry",
    }, {
        "name": "L1 Selected Elements",
        "level": 1,
        "subject": "chemistry",
    }, {
        "name": "L1 Chemical Reactions",
        "level": 1,
        "subject": "chemistry",
    },
];

How can I sort by subject, then level, then name?

guest271314
  • 1
  • 15
  • 104
  • 177
DarkMatterMatt
  • 576
  • 10
  • 22
  • Maybe sort by subject first, then take all items with the same subject into a new list and sort that by level, then do the same with name, in the end piece it back together? – xDreamCoding Jun 04 '17 at 05:08
  • Strings need to be compared with [`a.subject.localeCompare(b.subject)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare). The sort function needs to return a positive or negative value or zero. Currently for strings it’s only returning `true` or `false` which reflects positive and zero. Negative is missing. – Sebastian Simon Jun 04 '17 at 05:08
  • So if it's false it should return `-1`? – DarkMatterMatt Jun 04 '17 at 05:09
  • 2
    @DarkMatterMatt It needs to return `-1` whenever the first value needs to come _before_ the second one. – Sebastian Simon Jun 04 '17 at 05:10
  • You could probably simplify everything into `scipads.sort((a, b) => a.subject.localeCompare(b.subject) || a.level - b.level || a.name.localeCompare(b.name));`. – Sebastian Simon Jun 04 '17 at 05:12
  • Yeah, but performance isn't a priority, and it's easier for me (a beginner) to read shorter lines in a if else way. An explanation and example using `||` can be found at https://stackoverflow.com/a/27993435/6595777 – DarkMatterMatt Jun 04 '17 at 05:29

5 Answers5

2

Use localeCompare for comparing strings

var scipads=[{name:"L2 Physics Externals",level:2,subject:"physics"},{name:"L2 Physics Internals",level:2,subject:"physics"},{name:"L2 Chem Externals",level:2,subject:"chemistry"},{name:"L2 Chem Internals",level:2,subject:"chemistry"},{name:"L2 Bio Internals",level:2,subject:"biology"},{name:"L2 Bio Externals",level:2,subject:"biology"},{name:"L1 Electricity & Magnetism",level:1,subject:"physics"},{name:"L1 Wave Behaviour",level:1,subject:"physics"},{name:"L1 Heat",level:1,subject:"physics"},{name:"L1 Carbon Chemistry",level:1,subject:"chemistry"},{name:"L1 Selected Elements",level:1,subject:"chemistry"},{name:"L1 Chemical Reactions",level:1,subject:"chemistry"}];

scipads.sort(function(a, b) {
  if (a.subject != b.subject)
    return a.subject.localeCompare(b.subject);
  else if (a.level != b.level)
    return a.level - b.level;
  return a.name.localeCompare(b.name);
});

console.log(scipads);
Rajesh
  • 24,354
  • 5
  • 48
  • 79
A. L
  • 11,695
  • 23
  • 85
  • 163
2

Comparison should return an integer. Here's an example:

scipads.sort((a, b) => {
    if (a.subject != b.subject) { 
        return a.subject < b.subject ? -1 : 1; 
    }
    if (a.level != b.level) { 
        return a.level - b.level; 
    }
    return a.name < b.name ? -1 : 1;
})
Andy Gaskell
  • 31,495
  • 6
  • 74
  • 83
1

Your sort compare function is close, but not quite on. You should be returning -1 for less than, 0 for equal, and 1 for greater than. See here for documentation.

Example: change return a.subject < b.subject to return a.subject < b.subject ? -1 : 1;

pacifier21
  • 813
  • 1
  • 5
  • 13
0

My solution, compare subject first, then compare the level

var scipads=[{name:"L2 Physics Externals",level:2,subject:"physics"},{name:"L2 Physics Internals",level:2,subject:"physics"},{name:"L2 Chem Externals",level:2,subject:"chemistry"},{name:"L2 Chem Internals",level:2,subject:"chemistry"},{name:"L2 Bio Internals",level:2,subject:"biology"},{name:"L2 Bio Externals",level:2,subject:"biology"},{name:"L1 Electricity & Magnetism",level:1,subject:"physics"},{name:"L1 Wave Behaviour",level:1,subject:"physics"},{name:"L1 Heat",level:1,subject:"physics"},{name:"L1 Carbon Chemistry",level:1,subject:"chemistry"},{name:"L1 Selected Elements",level:1,subject:"chemistry"},{name:"L1 Chemical Reactions",level:1,subject:"chemistry"}];

// sort by subject, then level, then name
var res = scipads.sort((a, b) => {
  if (a.subject > b.subject) {
    return 1;
  } else if (a.subject < b.subject) {
    return -1;
  } else if ( a.level > b.level){
    return 1;
  } else if (a.level < b.level) {
    return -1;
  } else if (a.name > b.name) {
    return 1;
  } else if (a.name < b.name) {
    return -1;
  }
  return 0;
});


console.log(res)
Rajesh
  • 24,354
  • 5
  • 48
  • 79
Mμ.
  • 8,382
  • 3
  • 26
  • 36
0

To compare string, you should use string.localeCompare. Also, you can try to create an array with priority list and loop over it to sort.

var scipads=[{name:"L2 Physics Externals",level:2,subject:"physics"},{name:"L2 Physics Internals",level:2,subject:"physics"},{name:"L2 Chem Externals",level:2,subject:"chemistry"},{name:"L2 Chem Internals",level:2,subject:"chemistry"},{name:"L2 Bio Internals",level:2,subject:"biology"},{name:"L2 Bio Externals",level:2,subject:"biology"},{name:"L1 Electricity & Magnetism",level:1,subject:"physics"},{name:"L1 Wave Behaviour",level:1,subject:"physics"},{name:"L1 Heat",level:1,subject:"physics"},{name:"L1 Carbon Chemistry",level:1,subject:"chemistry"},{name:"L1 Selected Elements",level:1,subject:"chemistry"},{name:"L1 Chemical Reactions",level:1,subject:"chemistry"}];

var priority = ["subject", "level", "name"]

scipads.sort(function(a,b){
  var val = 0;
  priority.some(function(k){
    val = compare(a,b,k);
    return val !== 0;
  });
})

function compare(a,b,k){
  switch(typeof a[k]){
    case "string": return a[k].localeCompare(b[k]);
    case "number": return a[k] - b[k]
  }
}

console.log(scipads)
Rajesh
  • 24,354
  • 5
  • 48
  • 79