Sort an array of objects by two keys BUT push ‘other’ to end of first key’s section

How would I sort the first array into a final product that reflects the second array, please?

(I’m sorting first by Category but then I’m sorting by Subcategory while pushing ‘Other’ to the end of the Category’s section.)

Input:

var unsorted = [
    {category: 'Computer', subcategory: 'Laptop'},
    {category: 'Computer', subcategory: 'Other'},
    {category: 'Computer', subcategory: 'Desktop'},
    {category: 'Network Device', subcategory: 'Gateway'},
    {category: 'Computer', subcategory: 'Virtual'},
    {category: 'Network Device', subcategory: 'Other'}
]

Desired Output:

var sorted = [
    {category: 'Computer', subcategory: 'Desktop'},
    {category: 'Computer', subcategory: 'Laptop'},
    {category: 'Computer', subcategory: 'Virtual'},
    {category: 'Computer', subcategory: 'Other'},
    {category: 'Network Device', subcategory: 'Gateway'},
    {category: 'Network Device', subcategory: 'Other'}
]

I’ve seen great answers for double-sorting and for pushing certain values to the end, but I haven’t seen an answer for doing both at once in this way.

Thank you for your time!

Kind Regards, Joseph

Answer

You need a sorting function that…

  • compares categories (with localeCompare()) first, returns result immediately if it’s not 0 (meaning categories are different)
  • checks subcategory of the first argument, returns 1 (meaning a > b) if it’s “Other”
  • checks subcategory of the second argument, returns -1 (meaning a < b) if it’s “Other”
  • if all previous checks fail, returns result of subcategories comparison

Here’s one possible way to implement it:

var unsorted = [
    {category: 'Computer', subcategory: 'Laptop'},
    {category: 'Computer', subcategory: 'Other'},
    {category: 'Computer', subcategory: 'Desktop'},
    {category: 'Network Device', subcategory: 'Gateway'},
    {category: 'Computer', subcategory: 'Virtual'},
    {category: 'Network Device', subcategory: 'Other'}
]

const sorted = unsorted.slice() // creating a copy to avoid mutating the original
.sort((
  {category: ca, subcategory: sca}, 
  {category: cb, subcategory: scb}
) => ca.localeCompare(cb) || 
  (sca === 'Other' ? 1 : scb === 'Other' ? -1 : sca.localeCompare(scb)));

console.log(sorted);

In this function ca and sca are values (assigned through so-called “parameter fields unpacking“) of category and subcategory of the first param of sorting function, cb and scb – the same fields of the second param.