62

I have a very simple JSON object like the following:

{
   "people":[
      {
         "f_name":"john",
         "l_name":"doe",
         "sequence":"0",
         "title":"president",
         "url":"google.com",
         "color":"333333"
      },
      {
         "f_name":"michael",
         "l_name":"goodyear",
         "sequence":"0",
         "title":"general manager",
         "url":"google.com",
         "color":"333333"
      }
   ]
}

Now that this is returned from my server side code, I run jQuery.each to form the necessary html and output the result.

Right now what I am doing is sending an AJAX call to the server containing my sort info... e.g. "Title DESC" and re-run an SQL query to return the new result set. But I want to avoid this and use jQuery to sort the resulting JSON to prevent round trips to the server, and multiple database access.

How can I achieve this using jQuery?

Hugolpz
  • 17,296
  • 26
  • 100
  • 187
Subliminal Hash
  • 13,614
  • 20
  • 73
  • 104
  • 1
    Just FYI... you can use any tutorial on the internet about javascript array sorting and it will apply directly to your scenario since JSON is simply a javascript object/array. JS makes no differentiation between objects and arrays since everything is basically an object in JS anyways. :-) – KyleFarris May 19 '09 at 17:57
  • You might want to try a variation of this: http://www.wrichards.com/blog/2009/02/jquery-sorting-elements/ – Tahir Akhtar May 19 '09 at 08:13
  • @Emin, please consider changing the accepted answer, since the answer I provided has now received almost twice as many upvotes as the previously accepted answer. – Sean the Bean May 29 '13 at 19:45
  • @SeantheBean true.. Did it.. – Subliminal Hash May 30 '13 at 06:31
  • 1
    Possible duplicate of [Sort array of objects by string property value in JavaScript](https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value-in-javascript) – georg Jan 06 '18 at 14:42

6 Answers6

127

jQuery isn't particularly helpful for sorting, but here's an elegant and efficient solution. Just write a plain JS function that takes the property name and the order (ascending or descending) and calls the native sort() method with a simple comparison function:

var people = [
    {
        "f_name": "john",
        "l_name": "doe",
        "sequence": "0",
        "title" : "president",
        "url" : "google.com",
        "color" : "333333",
    }
    // etc
];

function sortResults(prop, asc) {
    people.sort(function(a, b) {
        if (asc) {
            return (a[prop] > b[prop]) ? 1 : ((a[prop] < b[prop]) ? -1 : 0);
        } else {
            return (b[prop] > a[prop]) ? 1 : ((b[prop] < a[prop]) ? -1 : 0);
        }
    });
    renderResults();
}

Then:

sortResults('l_name', true);

Play with a working example here.

Sean the Bean
  • 5,222
  • 5
  • 38
  • 40
  • 2
    This doesn't seem to be working for me when I put in a few more rows of data. It seems to sort differently everytime I try. Here is the fiddle: http://jsfiddle.net/Jsar8/ Any help is appreciated. – Jeff Borden Dec 19 '12 at 20:56
  • 1
    Surprisingly, Chrome (as well as some other major browsers) do not use a "stable" (i.e. consistent?) sorting algorithm. The following questions have some good discussion on browsers' sorting methods: http://stackoverflow.com/questions/1427608/fast-stable-sorting-algorithm-implementation-in-javascript http://stackoverflow.com/questions/234683/javascript-array-sort-implementation http://stackoverflow.com/questions/3026281/array-sort-sorting-stability-in-different-browsers – Sean the Bean Dec 22 '12 at 06:31
  • **Important limit:** The core sort function worked on FF, fails on Chrome 24, fails on Android default browser. http://jsfiddle.net/VAKrE/377/ The function was otherwise elegant ! – Hugolpz Feb 19 '13 at 03:24
  • 2
    @Hugolpz To be precise, Chrome, Opera <10, Dolphin, and FF <3 implement unstable sorting algorithms (according to http://stackoverflow.com/questions/3026281 and the best information I could find across the internet). The algorithms used in FF 3+, Opera 10+, IE, and Safari are stable. The related SO questions in my previous comment go much more in depth about Array.sort() implementations. – Sean the Bean Feb 20 '13 at 06:21
  • It's worth pointing out that the ECMAScript standard specifies that comparisons should return a number (positive, negative, or zero) rather than a boolean value. Since boolean false evaluates to zero, `return a>b` will effectively say that a and b are equal if b>=a. (See http://stackoverflow.com/q/1969145/509813). I haven't tested, but I suspect modifying this function to return the appropriate numerical value will fix it. – eaj Apr 17 '13 at 17:25
  • 1
    @eaj You are correct in pointing out the ECMAScript standard - that was an oversight on my part - and I will update the code accordingly. The result is effectively the same, though, as you can see here: http://jsfiddle.net/Jsar8/1/ (based on Jeff Borden's example of the issue). Whenever possible, please provide a working example with your suggestion. – Sean the Bean Apr 20 '13 at 15:49
  • Great solution! If you want to sort integers, just add parseInt to a[prop] and b[prop]. Or add a parameter to the function to pass the type of values to compare and do the right parsing with a switch statement. – krlzlx Oct 13 '15 at 08:17
  • This works for me, except that 1000000 sorts with 100000. When in reality, 1000000 needs to be sorted as 1000000, 999999, 888888, etc. I'm using this function for sorting Real Estate prices. Thoughts? – dcolumbus Dec 09 '16 at 21:30
  • @dcolumbus Based on your brief description, the comparison taking place is `1000000 > 100000` which evaluates to `true`. I'd need more information to be able to help you figure out what's wrong. Perhaps you can provide an example of your code on https://jsfiddle.net/ or post a new question to get further help? – Sean the Bean Dec 11 '16 at 03:37
  • really Nice concepts – SantoshK Sep 03 '19 at 10:42
30

Demo: https://jsfiddle.net/kvxazhso/

Successfully pass equal values (keep same order). Flexible : handle ascendant (123) or descendant (321), works for numbers, letters, and unicodes. Works on all tested devices (Chrome, Android default browser, FF).

Given data such :

var people = [ 
{ 'myKey': 'A', 'status': 0 },
{ 'myKey': 'B', 'status': 3 },
{ 'myKey': 'C', 'status': 3 },
{ 'myKey': 'D', 'status': 2 },
{ 'myKey': 'E', 'status': 7 },
...
];

Sorting by ascending or reverse order:

function sortJSON(arr, key, way) {
    return arr.sort(function(a, b) {
        var x = a[key]; var y = b[key];
        if (way === '123') { return ((x < y) ? -1 : ((x > y) ? 1 : 0)); }
        if (way === '321') { return ((x > y) ? -1 : ((x < y) ? 1 : 0)); }
    });
}

people2 = sortJSON(people,'status', '321'); // 123 or 321
alert("2. After processing (0 to x if 123; x to 0 if 321): "+JSON.stringify(people2));
Hugolpz
  • 17,296
  • 26
  • 100
  • 187
12
jQuery.fn.sort = function() {  
    return this.pushStack( [].sort.apply( this, arguments ), []);  
};  

 function sortLastName(a,b){  
     if (a.l_name == b.l_name){
       return 0;
     }
     return a.l_name> b.l_name ? 1 : -1;  
 };  
  function sortLastNameDesc(a,b){  
     return sortLastName(a,b) * -1;  
 };
var people= [
{
"f_name": "john",
"l_name": "doe",
"sequence": "0",
"title" : "president",
"url" : "google.com",
"color" : "333333",
},
{
"f_name": "michael",
"l_name": "goodyear",
"sequence": "0",
"title" : "general manager",
"url" : "google.com",
"color" : "333333",
}]

sorted=$(people).sort(sortLastNameDesc);  
Tahir Akhtar
  • 11,385
  • 7
  • 42
  • 69
  • See my adaptation of Bill Richards code. It's sorting json array based on last name in descending order. The sort callback can be generalized by passing the field name in constructor of function. – Tahir Akhtar May 19 '09 at 10:35
  • 1
    You have to replace innerHTML with l_name in sortLastName – Magnar May 19 '09 at 10:40
  • Just to clarify, my first comment, what I mean is that this code can be further modified to support generic sorting. – Tahir Akhtar May 19 '09 at 11:59
  • Shouldn't sortLastName() return 0 if a.l_name and b.l_name are equal? Perhaps because of the data, it is unnecessary in this case, but reading the code, I can't help thinking of this: http://blogs.msdn.com/oldnewthing/archive/2009/05/08/9595334.aspx ... "First of all, your first comparison function clearly violates the requirements for being a comparison function: It must return zero if the two items compare equal." – Grant Wagner May 19 '09 at 20:46
  • Yes you are right. It should return 0 when both are equal. As I said it was a quick adaptation of code found elsewhere. Purpose was just to show the concept so I didn't look closely enough. – Tahir Akhtar May 20 '09 at 09:51
  • Thanks for this Tahir, saved me a lot of faffing about looking for something. – Ian Devlin Feb 20 '12 at 10:27
9

If you don't mind using an external library, Lodash has lots of wonderful utilities

var people = [
  {
     "f_name":"john",
     "l_name":"doe",
     "sequence":"0",
     "title":"president",
     "url":"google.com",
     "color":"333333"
  },
  {
     "f_name":"michael",
     "l_name":"goodyear",
     "sequence":"0",
     "title":"general manager",
     "url":"google.com",
     "color":"333333"
  }
];


var sorted = _.sortBy(people, "l_name")

You can also sort by multiple properties. Here's a plunk showing it in action

tcql
  • 106
  • 1
  • 3
1

Solution working with different types and with upper and lower cases.
For example, without the toLowerCase statement, "Goodyear" will come before "doe" with an ascending sort. Run the code snippet at the bottom of my answer to view the different behaviors.

JSON DATA:

var people = [
{
    "f_name" : "john",
    "l_name" : "doe", // lower case
    "sequence": 0 // int
},
{
    "f_name" : "michael",
    "l_name" : "Goodyear", // upper case
    "sequence" : 1 // int
}];

JSON Sort Function:

function sortJson(element, prop, propType, asc) {
  switch (propType) {
    case "int":
      element = element.sort(function (a, b) {
        if (asc) {
          return (parseInt(a[prop]) > parseInt(b[prop])) ? 1 : ((parseInt(a[prop]) < parseInt(b[prop])) ? -1 : 0);
        } else {
          return (parseInt(b[prop]) > parseInt(a[prop])) ? 1 : ((parseInt(b[prop]) < parseInt(a[prop])) ? -1 : 0);
        }
      });
      break;
    default:
      element = element.sort(function (a, b) {
        if (asc) {
          return (a[prop].toLowerCase() > b[prop].toLowerCase()) ? 1 : ((a[prop].toLowerCase() < b[prop].toLowerCase()) ? -1 : 0);
        } else {
          return (b[prop].toLowerCase() > a[prop].toLowerCase()) ? 1 : ((b[prop].toLowerCase() < a[prop].toLowerCase()) ? -1 : 0);
        }
      });
  }
}

Usage:

sortJson(people , "l_name", "string", true);
sortJson(people , "sequence", "int", true);

var people = [{
  "f_name": "john",
  "l_name": "doe",
  "sequence": 0
}, {
  "f_name": "michael",
  "l_name": "Goodyear",
  "sequence": 1
}, {
  "f_name": "bill",
  "l_name": "Johnson",
  "sequence": 4
}, {
  "f_name": "will",
  "l_name": "malone",
  "sequence": 2
}, {
  "f_name": "tim",
  "l_name": "Allen",
  "sequence": 3
}];

function sortJsonLcase(element, prop, asc) {
  element = element.sort(function(a, b) {
    if (asc) {
      return (a[prop] > b[prop]) ? 1 : ((a[prop] < b[prop]) ? -1 : 0);
    } else {
      return (b[prop] > a[prop]) ? 1 : ((b[prop] < a[prop]) ? -1 : 0);
    }
  });
}

function sortJson(element, prop, propType, asc) {
  switch (propType) {
    case "int":
      element = element.sort(function(a, b) {
        if (asc) {
          return (parseInt(a[prop]) > parseInt(b[prop])) ? 1 : ((parseInt(a[prop]) < parseInt(b[prop])) ? -1 : 0);
        } else {
          return (parseInt(b[prop]) > parseInt(a[prop])) ? 1 : ((parseInt(b[prop]) < parseInt(a[prop])) ? -1 : 0);
        }
      });
      break;
    default:
      element = element.sort(function(a, b) {
        if (asc) {
          return (a[prop].toLowerCase() > b[prop].toLowerCase()) ? 1 : ((a[prop].toLowerCase() < b[prop].toLowerCase()) ? -1 : 0);
        } else {
          return (b[prop].toLowerCase() > a[prop].toLowerCase()) ? 1 : ((b[prop].toLowerCase() < a[prop].toLowerCase()) ? -1 : 0);
        }
      });
  }
}

function sortJsonString() {
  sortJson(people, 'l_name', 'string', $("#chkAscString").prop("checked"));
  display();
}

function sortJsonInt() {
  sortJson(people, 'sequence', 'int', $("#chkAscInt").prop("checked"));
  display();
}

function sortJsonUL() {
  sortJsonLcase(people, 'l_name', $('#chkAsc').prop('checked'));
  display();
}

function display() {
  $("#data").empty();
  $(people).each(function() {
    $("#data").append("<div class='people'>" + this.l_name + "</div><div class='people'>" + this.f_name + "</div><div class='people'>" + this.sequence + "</div><br />");
  });
}
body {
  font-family: Arial;
}
.people {
  display: inline-block;
  width: 100px;
  border: 1px dotted black;
  padding: 5px;
  margin: 5px;
}
.buttons {
  border: 1px solid black;
  padding: 5px;
  margin: 5px;
  float: left;
  width: 20%;
}
ul {
  margin: 5px 0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="buttons" style="background-color: rgba(240, 255, 189, 1);">
  Sort the JSON array <strong style="color: red;">with</strong> toLowerCase:
  <ul>
    <li>Type: string</li>
    <li>Property: lastname</li>
  </ul>
  <button onclick="sortJsonString(); return false;">Sort JSON</button>
  Asc Sort
  <input id="chkAscString" type="checkbox" checked="checked" />
</div>
<div class="buttons" style="background-color: rgba(255, 214, 215, 1);">
  Sort the JSON array <strong style="color: red;">without</strong> toLowerCase:
  <ul>
    <li>Type: string</li>
    <li>Property: lastname</li>
  </ul>
  <button onclick="sortJsonUL(); return false;">Sort JSON</button>
  Asc Sort
  <input id="chkAsc" type="checkbox" checked="checked" />
</div>
<div class="buttons" style="background-color: rgba(240, 255, 189, 1);">
  Sort the JSON array:
  <ul>
    <li>Type: int</li>
    <li>Property: sequence</li>
  </ul>
  <button onclick="sortJsonInt(); return false;">Sort JSON</button>
  Asc Sort
  <input id="chkAscInt" type="checkbox" checked="checked" />
</div>
<br />
<br />
<div id="data" style="float: left; border: 1px solid black; width: 60%; margin: 5px;">Data</div>
krlzlx
  • 5,752
  • 14
  • 47
  • 55
0

Here's a multiple-level sort method. I'm including a snippet from an Angular JS module, but you can accomplish the same thing by scoping the sort keys objects such that your sort function has access to them. You can see the full module at Plunker.

$scope.sortMyData = function (a, b)
{
  var retVal = 0, key;
  for (var i = 0; i < $scope.sortKeys.length; i++)
  {
    if (retVal !== 0)
    {
      break;
    }
    else
    {
      key = $scope.sortKeys[i];
      if ('asc' === key.direction)
      {
        retVal = (a[key.field] < b[key.field]) ? -1 : (a[key.field] > b[key.field]) ? 1 : 0;
      }
      else
      {
        retVal = (a[key.field] < b[key.field]) ? 1 : (a[key.field] > b[key.field]) ? -1 : 0;
      }
    }
  }
  return retVal;
};
KSev
  • 1,859
  • 1
  • 24
  • 23