user8571142
user8571142

Reputation: 91

Sorting array by 2 conditions in Javascript not working

I want to sort an array of objects by both id and name. First it should be sorted by id in ascending order. Then it should be sorted by name in ascending order.

Sort function

<script>
  $(document).ready(function() {
    var list = [{
      "id": "ts1",
      "name": "TS1"
    }, {
      "id": "ts10",
      "name": "TS10"
    }, {
      "id": "ts11",
      "name": "TS11"
    }, {
      "id": "ts12",
      "name": "TS12"
    }, {
      "id": "ts13",
      "name": "TS2"
    }, {
      "id": "ts13",
      "name": "TS1"
    }, {
      "id": "ts13",
      "name": "TS3"
    }, {
      "id": "ts14",
      "name": "TS14"
    }, {
      "id": "ts15",
      "name": "TS15"
    }, {
      "id": "ts16",
      "name": "TS16"
    }, {
      "id": "ts17",
      "name": "TS17"
    }, {
      "id": "ts18",
      "name": "TS18"
    }, {
      "id": "ts19",
      "name": "TS19"
    }, {
      "id": "ts2",
      "name": "TS2"
    }, {
      "id": "ts20",
      "name": "TS20"
    }, {
      "id": "ts21",
      "name": "TS21"
    }, {
      "id": "ts22",
      "name": "TS22"
    }, {
      "id": "ts3",
      "name": "TS3"
    }, {
      "id": "ts4",
      "name": "TS4"
    }, {
      "id": "ts5",
      "name": "TS5"
    }, {
      "id": "ts6",
      "name": "TS6"
    }, {
      "id": "ts7",
      "name": "TS7"
    }]

    list.sort(Sort_ID_Name);

    console.log(list);
  });

  function Sort_ID_Name(a, b) {
    try {
      var aID = a.id.toLowerCase();
      var bID = b.id.toLowerCase();
      var aName = a.name.toLowerCase();
      var bName = b.name.toLowerCase();
      return ((aID < bID) ? -1 : ((aID > bID) ? 1 : ((aName < bName) ? -1 : ((aName > bName) ? 1 : 0))));
    } catch (err) {}
  }

</script>

Currently the sorted list shows up like this:

{id: "ts1", name: "TS1"}
{id: "ts10", name: "TS10"}
{id: "ts11", name: "TS11"}
{id: "ts12", name: "TS12"}
{id: "ts13", name: "TS1"}
{id: "ts13", name: "TS2"}
{id: "ts13", name: "TS3"}
{id: "ts14", name: "TS14"}
{id: "ts15", name: "TS15"}
{id: "ts16", name: "TS16"}
....

The ideal state is like this

{id: "ts1", name: "TS1"}
{id: "ts2", name: "TS2"}
...
{id: "ts10", name: "TS10"}
{id: "ts11", name: "TS11"}
{id: "ts12", name: "TS12"}
{id: "ts13", name: "TS1"}
{id: "ts13", name: "TS2"}
{id: "ts13", name: "TS3"}
{id: "ts14", name: "TS14"}
{id: "ts15", name: "TS15"}
{id: "ts16", name: "TS16"}
....

The function works only when the list is very short, but when the list is long it doesn't work well. See live demo- https://jsfiddle.net/0xLd6sms/2/

Upvotes: 0

Views: 162

Answers (2)

Jared Smith
Jared Smith

Reputation: 21926

You're doing >/< comparisons with strings. Those work on unicode point values, which is rarely what you want. Change this:

  var aID = a.id.toLowerCase();
  var bID = b.id.toLowerCase();
  var aName = a.name.toLowerCase();
  var bName = b.name.toLowerCase();

To this:

  var reg = /\d+/; // matches 1 or more number characters
  var aID = +a.id.match(reg)[0]; // unary + coerces to number
  var bID = +b.id.match(reg)[0];
  var aName = +a.name.match(reg)[0];
  var bName = +b.name.match(reg)[0];

Now you're comparisons will be on the actual number values in the strings.

Upvotes: 0

Hassan Imam
Hassan Imam

Reputation: 22534

You can use custom sort method using localeComapre() specifying the numeric: true option, it will smartly recognize numbers

var list = [{ "id": "ts1", "name": "TS1" }, { "id": "ts10", "name": "TS10" }, { "id": "ts11", "name": "TS11" }, { "id": "ts12", "name": "TS13" }, { "id": "ts13", "name": "TS2" }, { "id": "ts13", "name": "TS1" }, { "id": "ts13", "name": "TS3" }, { "id":"ts14", "name": "TS14" }, { "id": "ts15", "name": "TS15" }, { "id": "ts16", "name": "TS16" }, { "id": "ts17", "name": "TS17" }, { "id": "ts18", "name": "TS18" }, { "id": "ts19", "name": "TS19" }, { "id": "ts2", "name": "TS2" }, { "id": "ts20", "name":"TS20" }, { "id": "ts21", "name": "TS21" }, { "id": "ts22", "name": "TS22" }, { "id": "ts3", "name": "TS3" }, { "id": "ts4", "name": "TS4" }, { "id": "ts5", "name": "TS5" }, { "id": "ts6", "name": "TS6" }, { "id": "ts7", "name": "TS7" }];
list.sort((a,b) => a.id.localeCompare(b.id, undefined, {numeric: true}) || a.name.localeCompare(b.name, undefined, {numeric: true}));
console.log(list);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Related Questions