按值对JSON进行排序

62

我有一个非常简单的JSON对象,如下所示:

{
   "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"
      }
   ]
}
现在我的服务器端代码已经返回了这个结果,我使用jQuery.each来形成所需的HTML并输出结果。 目前我正在发送一个AJAX调用到服务器,其中包含我的排序信息...例如"Title DESC",然后重新运行SQL查询以返回新的结果集。但我想避免这样做,并使用jQuery来对生成的JSON进行排序,以防止往返服务器和多次访问数据库。我如何使用jQuery实现这一点?

你可能想尝试这个变体:http://www.wrichards.com/blog/2009/02/jquery-sorting-elements/ - Tahir Akhtar
1
只是提一下... 你可以使用互联网上关于 JavaScript 数组排序的任何教程,它们都可以直接应用于你的场景,因为 JSON 只是一个 JavaScript 对象/数组。JS 在本质上没有区分对象和数组,因为在 JS 中一切都基本上是对象。:-) - KyleFarris
@ Emin,请考虑更改已接受的答案,因为我提供的答案现在已经获得了几乎是先前接受的答案两倍的赞同数。 - Sean the Bean
@SeantheBean 确实。已完成。 - Subliminal Hash
6个回答

127

jQuery 在排序方面并不特别有帮助,但这里有一个优雅而高效的解决方案。只需编写一个纯 JS 函数,它接受属性名称和顺序(升序或降序),并使用简单的比较函数调用本地 sort() 方法:

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();
}

那么:

sortResults('l_name', true);

在这里玩一个可用的示例


2
当我添加了更多的数据行时,这似乎对我不起作用。每次尝试时,它似乎都会以不同的方式进行排序。这是 jsfiddle 的链接:http://jsfiddle.net/Jsar8/。任何帮助将不胜感激。 - Jeff Borden
1
令人惊讶的是,Chrome(以及其他一些主要浏览器)并未使用“稳定”的(即一致的?)排序算法。以下问题对浏览器的排序方法进行了一些良好的讨论:https://dev59.com/qnM_5IYBdhLWcg3wUxZB https://dev59.com/ZXVC5IYBdhLWcg3woCnN https://dev59.com/QnA75IYBdhLWcg3w790Y - Sean the Bean
2
@Hugolpz 更准确地说,Chrome、Opera <10、Dolphin 和 FF <3 实现了不稳定的排序算法(根据 https://dev59.com/QnA75IYBdhLWcg3w790Y 和我在互联网上找到的最好信息)。FF 3+、Opera 10+、IE 和 Safari 中使用的算法是稳定的。我之前评论中相关的 SO 问题更深入地探讨了 Array.sort() 的实现。 - Sean the Bean
1
@eaj,你指出ECMAScript标准是正确的 - 这是我的疏忽 - 我会相应地更新代码。结果实际上是相同的,如您在此处所见:http://jsfiddle.net/Jsar8/1/(基于Jeff Borden的问题示例)。请尽可能提供一个带有您建议的工作示例。 - Sean the Bean
很棒的解决方案!如果你想要对整数进行排序,只需在 a[prop] 和 b[prop] 上添加 parseInt。或者在函数中添加一个参数来传递要比较的值的类型,并使用 switch 语句进行正确的解析。 - krlzlx
显示剩余5条评论

30

示例代码:https://jsfiddle.net/kvxazhso/

成功地通过相等的值(保持相同的顺序)。灵活:处理升序(123)或降序(321),适用于数字,字母和 Unicode。在所有测试设备上均可使用(Chrome,Android 默认浏览器,FF)。

给定数据如下

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

按升序或降序排序:

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));

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);  

见我的 Bill Richards 代码改编。它可以根据姓氏降序排序JSON数组。可以通过在函数构造函数中传递字段名来通用排序回调。 - Tahir Akhtar
1
你需要将innerHTML替换为l_name以在sortLastName中实现功能。 - Magnar
只是想澄清一下,我的第一条评论的意思是这段代码可以进一步修改以支持通用排序。 - Tahir Akhtar
如果a.l_name和b.l_name相等,sortLastName()不应该返回0吗?也许因为数据的原因,在这种情况下这是不必要的,但是阅读代码时,我不禁想到了这个:http://blogs.msdn.com/oldnewthing/archive/2009/05/08/9595334.aspx...“首先,你的第一个比较函数明显违反了比较函数的要求:如果两个项相等,则必须返回零。” - Grant Wagner
是的,你说得对。当两个相等时,它应该返回0。就像我说的,这只是在其他地方找到的代码的快速适应。目的只是为了展示概念,所以我没有仔细看。 - Tahir Akhtar
谢谢你的帮助,Tahir。省去了我很多寻找的麻烦。 - Ian Devlin

9
如果你不介意使用外部库,Lodash 有许多很棒的实用工具。
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")

您还可以按多个属性进行排序。 这里 是一个演示它工作的 plunk。

1

解决方案可处理不同类型的数据,包括大小写字母。
例如,在升序排序中,如果没有使用toLowerCase语句,"Goodyear"将排在"doe"之前。运行我回答底部的代码片段来查看不同的行为。

JSON数据:

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排序函数:

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);
        }
      });
  }
}

使用方法:

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>


0
这是一个多级排序方法。我包含了一个Angular JS模块的片段,但你可以通过将排序键对象作用域化,使你的排序函数可以访问它们来实现相同的功能。你可以在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;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接