how to sort array by nested properties

前端 未结 2 2018
悲哀的现实
悲哀的现实 2021-01-29 10:15



        
相关标签:
2条回答
  • 2021-01-29 10:38

    You could define your compare functions in a separate object, and reference the one your need when you call sort:

    Here is a snippet that does that:

    const array = [{"id":248439,"name":"Cross Creek Ranch/Creek Cove","surveyStatus":{"territoryName":"Fulshear","subdivisionName":"Cross Creek Ranch/Creek Cove","subdivisionId":248439,"dateTimeAdded":null,"surveyStatus":"0"}},{"id":248545,"name":"Lakes of Bella Terra/Via Verdone","surveyStatus":{"territoryName":"Fulshear","subdivisionName":"Lakes of Bella Terra/Via Verdone","subdivisionId":248545,"dateTimeAdded":null,"surveyStatus":"0"}},{"id":248546,"name":"Lakes of Bella Terra/Via Moderna","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-13 14:24:24.312","lng":-95.78459389953542,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"248546","surveyStatus":"2","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Lakes of Bella Terra/Via Moderna","dateTimeUploaded":"2017-03-13 14:24:24.316","lat":29.68844027643332}},{"id":248547,"name":"Lakes of Bella Terra/Via Privato","surveyStatus":{"territoryName":"Fulshear","subdivisionName":"Lakes of Bella Terra/Via Privato","subdivisionId":248547,"dateTimeAdded":null,"surveyStatus":"0"}},{"id":248548,"name":"Lakes of Bella Terra/Mirandola","surveyStatus":{"territoryName":"Fulshear","subdivisionName":"Lakes of Bella Terra/Mirandola","subdivisionId":248548,"dateTimeAdded":null,"surveyStatus":"0"}},{"id":248549,"name":"Lakes of Bella Terra/La Bella Cortile","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-13 14:38:22.958","lng":-95.78834879221002,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"248549","surveyStatus":"2","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Lakes of Bella Terra/La Bella Cortile","dateTimeUploaded":"2017-03-13 14:38:22.964","lat":29.69532227612072}},{"id":248838,"name":"Cross Creek Ranch/Pond","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-14 12:12:20.408","lng":-95.8761960827707,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"248838","surveyStatus":"1","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Cross Creek Ranch/Pond","dateTimeUploaded":"2017-03-14 12:12:20.416","lat":29.70182981810505}},{"id":249626,"name":"Cross Creek Ranch/Legacy-Herons Lake","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-14 13:11:24.276","lng":-95.8625899069904,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"249626","surveyStatus":"2","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Cross Creek Ranch/Legacy-Herons Lake","dateTimeUploaded":"2017-03-14 13:11:24.282","lat":29.70904039221789}},{"id":249727,"name":"Fulshear Run","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-14 11:49:22.765","lng":-95.8850889467596,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"249727","surveyStatus":"2","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Fulshear Run","dateTimeUploaded":"2017-03-14 11:49:22.772","lat":29.6824066434332}},{"id":249739,"name":"Lakes of Bella Terra/Cittanova","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-13 15:56:25.460","lng":-95.78473585666696,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"249739","surveyStatus":"2","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Lakes of Bella Terra/Cittanova","dateTimeUploaded":"2017-03-13 15:56:25.467","lat":29.69387132677221}},{"id":249883,"name":"Lakes of Bella Terra/Vita Bella","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-13 15:54:04.856","lng":-95.78164947228113,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"249883","surveyStatus":"1","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Lakes of Bella Terra/Vita Bella","dateTimeUploaded":"2017-03-13 15:54:04.864","lat":29.69463965392643}},{"id":249884,"name":"Lakes of Bella Terra/Valencia","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-13 14:32:35.471","lng":-95.78839095318297,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"249884","surveyStatus":"2","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Lakes of Bella Terra/Valencia","dateTimeUploaded":"2017-03-13 14:32:35.477","lat":29.69075137286418}},{"id":249885,"name":"Lakes of Bella Terra/Porte Toscana","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-13 14:10:33.875","lng":-95.79300477178376,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"249885","surveyStatus":"1","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Lakes of Bella Terra/Porte Toscana","dateTimeUploaded":"2017-03-13 14:10:33.882","lat":29.68849873638683}},{"id":249920,"name":"Cross Creek Ranch/The Falls","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-14 15:07:57.054","lng":-95.86257892669724,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"249920","surveyStatus":"2","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Cross Creek Ranch/The Falls","dateTimeUploaded":"2017-03-14 15:07:57.060","lat":29.73065241708395}},{"id":249941,"name":"Cross Creek Ranch/Heights","surveyStatus":{"territoryName":"Fulshear","dateTimeAdded":"2017-03-14 13:38:52.380","lng":-95.85420054392503,"userId":"6e77831f-9be5-41a4-8135-d961a94ef917","subdivisionId":"249941","surveyStatus":"2","territoryId":"4921","userName":"Michelle Artis","marketId":"13","subdivisionName":"Cross Creek Ranch/Heights","dateTimeUploaded":"2017-03-14 13:38:52.385","lat":29.71784273165252}}];
    
    const populate = (array) => {
        const rows = [];
        for (let i = 0; i < array.length; i++) {
            rows.push("<tr>",
                      "<td>",array[i].surveyStatus.subdivisionName,"</td>",
                      "<td>",array[i].surveyStatus.dateTimeAdded,"</td>",
                      "<td>",array[i].surveyStatus.surveyStatus,"</td>",
                      "</tr>");
        }
        document.getElementById("box").innerHTML = "<table border='1|1'>" +
            rows.join("") + "</table>";
    }
    
    const compare = {
        dateTimeAdded: (a, b) => // keep null at the end
            (Date.parse(a.surveyStatus.dateTimeAdded) || Infinity) - 
            (Date.parse(b.surveyStatus.dateTimeAdded) || Infinity),
        "-dateTimeAdded": (a, b) => // keep null at the end
            (Date.parse(b.surveyStatus.dateTimeAdded) || 0) - 
            (Date.parse(a.surveyStatus.dateTimeAdded) || 0),
        inProgress: (a, b) => b.surveyStatus.surveyStatus - a.surveyStatus.surveyStatus,
        notStarted: (a, b) => a.surveyStatus.surveyStatus - b.surveyStatus.surveyStatus,
        complete: (a, b) => // Give precedence to status 1
            (b.surveyStatus.surveyStatus + 1) % 3 - (a.surveyStatus.surveyStatus + 1) % 3,
    };
    
    const sortTable = (option, array) => populate(array.sort(compare[option]));
    
    const sort = document.getElementById('sort');
    sort.addEventListener('change', e => sortTable(sort.value, array) );
    sortTable(sort.value, array); // sort table at page load
    <select id="sort">
        <option value="complete">Sort Completed First</option>
        <option value="inProgress">Sort In-Progress First</option>
        <option value="notStarted">Sort Not-Started First</option>
        <option value="dateTimeAdded">Sort Added Asc</option>
        <option value="-dateTimeAdded">Sort Added Desc</option>
    </select>
    <div id= "box"></div>

    Note that sort provides a stable sort in most browsers, meaning that if you first sort by date, and then by a status, the records will still be sorted by date within the same status group.

    0 讨论(0)
  • In general, sorting by multiple criteria is done like this:

    theArray.sort(function(left, right) {
        var result = /*...compare first criterion*/;
        if (result == 0) {
            result = /*...compare second criterion*/;
            if (result == 0) {
                result = /*...compare third criterion*/;
                // ...and so on...
            }
        }
        return result;
    });
    

    ...where "compare X criterion" results in a negative number if left should come before right, 0 if they're the same for that criterion, and a positive number if right should come before left.

    That works because we only need to evaluate our second criterion if the entries are "the same" according to the first, only need to look at the third if the first and second are "the same," etc.

    That's for a hardcoded series. It can easily be adapted for a series defined by an array of comparisons to make (using a loop that breaks when result is not 0).

    Simple example of the hardcoded version:

    var array = [
      {a: 4, b: 3, c: 5},
      {a: 1, b: 2, c: 7},
      {a: 4, b: 4, c: 4},
      {a: 4, b: 4, c: 2},
      {a: 4, b: 4, c: 6},
      {a: 2, b: 8, c: 9},
      {a: 2, b: 9, c: 8}
    ];
    show("Unsorted", array);
    array.sort(function(left, right) {
      var result = left.a - right.a;
      if (result == 0) {
        result = left.b - right.b;
        if (result == 0) {
          result = left.c - right.c;
        }
      }
      return result;
    });
    show("Sorted", array);
    
    function show(label, a) {
      console.log(
        label,
        a.map(function(entry) {
          return "a:" + entry.a +
                 ",b:" + entry.b +
                 ",c:" + entry.c;
        })
      );
    }
    .as-console-wrapper {
      max-height: 100% !important;
    }

    0 讨论(0)
提交回复
热议问题