Home > Back-end >  Sorting multiple array elements alphabetically
Sorting multiple array elements alphabetically

Time:10-02

I was wondering if there was a better or smarter way of sorting objects in JavaScript when sorting for multiple items?

For example, if I have the following JSON:

json = [
 { "type": "car",   "year": 2007, "origin": "AUS" },
 { "type": "truck", "year": 2021, "origin": "USA" },
 { "type": "car",   "year": 2004, "origin": "CA"  },
 { "type": "bike",  "year": 2016, "origin": "UK"  },
 { "type": "truck", "year": 2020, "origin": "CA"  },
 { "type": "bike",  "year": 2000, "origin": "AUS" }
]

If I wanted to sort it by type then year how would I do that in the same .sort function?

I've currently got it doing one, then the other - but when I try to combine them it doesn't seem to output correctly:

// one after other
json.sort( ( a, b ) => {
 a = a.type.toLowerCase();
 b = b.type.toLowerCase();
 return a < b ? -1 : a > b ? 1 : 0;
})
.sort( ( a, b ) => {
 a = a.year;
 b = b.year;
 return a < b ? -1 : a > b ? 1 : 0;
});

Returning:

[
 { "type": "bike",  "year": 2000, "origin": "AUS" },
 { "type": "car",   "year": 2004, "origin": "CA"  },
 { "type": "car",   "year": 2007, "origin": "AUS" },
 { "type": "bike",  "year": 2016, "origin": "UK"  },
 { "type": "truck", "year": 2020, "origin": "USA" },
 { "type": "truck", "year": 2021, "origin": "CA"  }
]

When it should return (all the types together, then by year):

[
 { "type": "bike",  "year": 2000, "origin": "AUS" },
 { "type": "bike",  "year": 2016, "origin": "UK"  },
 { "type": "car",   "year": 2004, "origin": "CA"  },
 { "type": "car",   "year": 2007, "origin": "AUS" },
 { "type": "truck", "year": 2020, "origin": "USA" },
 { "type": "truck", "year": 2021, "origin": "CA"  }
]

CodePudding user response:

In the sort callback, compare the items' type property. If one is lexographically greater/smaller, sort it before/after the other item accordingly.

Otherwise, you can return the result after subtracting the two items' year property:

const arr = [
 { "type": "car",   "year": 2007, "origin": "AUS" },
 { "type": "truck", "year": 2021, "origin": "USA" },
 { "type": "car",   "year": 2004, "origin": "CA"  },
 { "type": "bike",  "year": 2016, "origin": "UK"  },
 { "type": "truck", "year": 2020, "origin": "CA"  },
 { "type": "bike",  "year": 2000, "origin": "AUS" }
]

const sorted = arr.sort((a,b) => a.type.localeCompare(b.type) || a.year - b.year)

console.log(sorted)

Credit to DraganS

If you don't want the comparison to be case-sensitive, you can set localeCompare's sensitivity option to accent:

const arr = [
 { "type": "car",   "year": 2007, "origin": "AUS" },
 { "type": "truck", "year": 2021, "origin": "USA" },
 { "type": "car",   "year": 2004, "origin": "CA"  },
 { "type": "bike",  "year": 2016, "origin": "UK"  },
 { "type": "truck", "year": 2020, "origin": "CA"  },
 { "type": "bike",  "year": 2000, "origin": "AUS" }
]

const sorted = arr.sort((a,b) => a.type.localeCompare(b.type, undefined, {sensitivity:'accent'}) || a.year - b.year)

console.log(sorted)

CodePudding user response:

You can do this with one sort by first checking if the types are equal. If so, sort on the date. If not, sort on the type.

const json = [
 { "type": "car",   "year": 2007, "origin": "AUS" },
 { "type": "truck", "year": 2021, "origin": "USA" },
 { "type": "car",   "year": 2004, "origin": "CA"  },
 { "type": "bike",  "year": 2016, "origin": "UK"  },
 { "type": "truck", "year": 2020, "origin": "CA"  },
 { "type": "bike",  "year": 2000, "origin": "AUS" }
]

// one after other
json.sort( ( a, b ) => {
 a = a.type.toLowerCase();
 b = b.type.toLowerCase();
 if (a === b) {
   return a.year - b.year;
 }
 return a < b ? -1 : 1;
});

console.log(json);

CodePudding user response:

If you do one sort followed by a different sort, the final sort overrides any previous sort.

Combine them into the same lambda


json.sort( ( a, b ) => {
 aType = a.type.toLowerCase();
 bType = b.type.toLowerCase();
 aYear = a.year;
 bYear = b.year;
 return aType < bType 
        ? -1 
        : aType > bType 
           ? 1 
           : aYear < bYear
              ? -1
              : aYear > bYear
                 ? 1
                 : 0;
})


Though this has gotten pretty unreadable. You can have multiple comparison functions:

let compareByType = (a, b) => {
     aType = a.type.toLowerCase();
     bType = b.type.toLowerCase();
     return (aType<bType) ? -1 : (aType>bType ? 1 : 0);
}

let compareByYear = (a,b) => {
     return a.year - b.year;
}

json.sort(
    (a, b) => {
       let s = compareByType(a, b);
       if(s !== 0) {
          return s;
       } else {
           return compareByYear(a, b);
       }
    }
);
  • Related