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?

6
  • 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? Commented Jun 4, 2017 at 5:08
  • Strings need to be compared with a.subject.localeCompare(b.subject). 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. Commented Jun 4, 2017 at 5:08
  • So if it's false it should return -1? Commented Jun 4, 2017 at 5:09
  • 2
    @DarkMatterMatt It needs to return -1 whenever the first value needs to come before the second one. Commented Jun 4, 2017 at 5: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));. Commented Jun 4, 2017 at 5:12

5 Answers 5

3

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

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

2 Comments

accepted your answer because of correct capitalization handling (and sorting the right way)
Thanks, localeCompare is useful for non-English characters too.
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;
})

5 Comments

hey, nice arrow operator. Learn something new every day.
@DarkMatterMatt The arrow operator is a feature called "Arrow Functions" introduced in ES6.
just for correctness, could you please edit the <s for >s. It's sorting Z-A.
@DarkMatterMatt can you double check? It's sorting correctly on my end :)
haha yep my bad, was thinking it was ? 1 : -1; not the other way round. Sorry :P
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;

2 Comments

Thanks, just got that myself :P (thanks Xufox). I can accept this answer in 5 mins.
Glad it came to you!
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)

Comments

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)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.