摘要:對數據進行排序是必不可少的功能。對簡單的名值對象按照指定屬性和排序方向進行排序根據排序屬性及排序方向,對兩個項依次進行比較,并返回代表排序位置的值。按照指定屬性及升降方向進行排序。
標簽:JavaScript 對象數組 排序
引言在以數據為中心的信息系統中,以表格形式展示數據是在常見不過的方式了。對數據進行排序是必不可少的功能。排序可以分為按單個字段排序和按多個字段不同排序方向排序。單字段排序局限性較大,不能滿足用戶對數據的關注點變化的需求,而多字段排序就可以較好的彌補這個缺陷。
多字段排序,實現的方式從大的層面上可以分為后端實現和前端實現。
后端排序后端實現排序可以在數據庫層面實現或者在應用程序層面實現。
數據庫層面實現多字段排序非常簡單,使用SQL的排序指令“Order By”即可——Order By field1 asc, field2 desc, field3 asc -- ...。
應用程序層面是指Web應用層(這里不討論C/S架構),比如PHP、Java Web、ASP.NET等。應用程序層面實現就是使用PHP、Java、.NET(C#/VB)這些后端服務語言來實現對數據的排序。以ASP.NET C# 為例,因為C#中的LINQ內置了對集合類型的諸多操作,并且支持多屬性排序,所以使用LINQ能夠很方便的實現此目的——from f in foos orderby f.Name descending, f.Num ascending select f(可以發現LINQ的排序語法幾乎與SQL的一模一樣)。如果其它語言沒有內置類似的支持,則按照排序算法來實現,這是通用的,與編程語言無關。
前端排序在JavaScript中,數組有一個排序方法“sort”,當數組是一個簡單數組(數組元素是簡單類型——字符串、數值和布爾)時,使用該方法可以很方便的到達排序目的。但是當數組元素是非簡單類型,比如名/值對的Object,并且想要按照指定的某幾個屬性按不同的排序方向進行排序時,簡單的調用“sort”方法就不能實現此目的了。
不過好在“sort”方法預留了自定義排序的接口,可以實現想要的排序方式。
來看看數組的“sort”方法是怎樣的。
sort函數原型// 對數組的元素做原地的排序,并返回這個數組。 // 默認按照字符串的Unicode碼位點(code point)排序。 Array.prototype.sort([compareFunction]:number); // number:-1 | 0 | 1。 // 典型的比較函數(升序排序)。 function compareFunction(item1, item2) { if(item1 > item2) { return 1; // 如果是降序排序,返回-1。 }else if(item1 === item2) { return 0; }else { return -1; // 如果是降序排序,返回1。 } }
說明:如果沒有指明compareFunction,那么元素會被轉換為字符串的諸個字符并按照Unicode位點順序排序。例如,"Cherry"會被排列到"banana"之前。當對數字進行排序的時候, 9 會出現在 80 之前,因為他們會先被轉換為字符串,而 "80" 比 "9" 要靠前。
如果 compareFunction(a, b) 小于 0 ,那么 a 會被排列到 b 之前;
如果 compareFunction(a, b) 等于 0 ,a 和 b
的相對位置不變。備注:ECMAScript標準并不保證這一行為,而且也不是所有瀏覽器都會遵守(例如 Mozilla 在 2003
年之前的版本);
如果 compareFunction(a, b) 大于 0 , b 會被排列到 a 之前。
compareFunction(a, b) 必須總是對相同的輸入返回相同的比較結果,否則排序的結果將是不確定的。
注:以上規則得出的排序結果是升序的,如果想要得到降序的結果,則在比較結果大于 0 時返回小于 0 的結果,比較結果小于 0 時 返回大于 0 的結果即可。
要實現多屬性排序,關鍵就在于比較函數的實現。根據以上規則, 實現多屬性不同方向排序,依然要返回兩個比較項的大小關系。那么多屬性對象的大小關系如何確定呢?這個可以分兩步走。
第一步,記錄下兩個排序項按照各個排序屬性及方向進行比較得到的結果。
var propOrders = { "prop1":"asc", "prop2":"desc", "prop3":"asc"}; function cmp(item1, item2, propOrders) { var cps = []; // 用于記錄各個排序屬性的比較結果,-1 | 0 | 1 。 var isAsc = true; // 排序方向。 for(var p in propOrders) { isAsc = propOrders[p] === "asc"; if(item1[p] > item2[p]) { cps.push(isAsc ? 1 : -1); break; // 可以跳出循環了,因為這里就已經知道 item1 “大于” item2 了。 } else if(item1[p] === item2[p]) { cps.push(0); } else { cps.push(isAsc ? -1 : 1); break; // 可以跳出循環,item1 “小于” item2。 } } /* . . . */ }
第二步,根據各排序屬性比較結果綜合判斷得出兩個比較項的最終大小關系。
/* . . . */ for(var j = 0; j < cps.length; j++) { if(cps[j] === 1 || cps[j] === -1) { return cps[j]; } } return 0;
有了上述思路后,實現整個比較函數就容易了,下面是比較函數的完整JavaScript代碼:
比較函數function SortByProps(item1, item2) { "use strict"; var props = []; for (var _i = 2; _i < arguments.length; _i++) { props[_i - 2] = arguments[_i]; } var cps = []; // 存儲排序屬性比較結果。 // 如果未指定排序屬性,則按照全屬性升序排序。 var asc = true; if (props.length < 1) { for (var p in item1) { if (item1[p] > item2[p]) { cps.push(1); break; // 大于時跳出循環。 } else if (item1[p] === item2[p]) { cps.push(0); } else { cps.push(-1); break; // 小于時跳出循環。 } } } else { for (var i = 0; i < props.length; i++) { var prop = props[i]; for (var o in prop) { asc = prop[o] === "asc"; if (item1[o] > item2[o]) { cps.push(asc ? 1 : -1); break; // 大于時跳出循環。 } else if (item1[o] === item2[o]) { cps.push(0); } else { cps.push(asc ? -1 : 1); break; // 小于時跳出循環。 } } } } for (var j = 0; j < cps.length; j++) { if (cps[j] === 1 || cps[j] === -1) { return cps[j]; } } return 0; }測試用例
// -------------測試用例------------------------------ var items = [ { name: "Edward", value: 21 }, { name: "Sharpe", value: 37 }, { name: "And", value: 45 }, { name: "Edward", value: -12 }, { name: "Magnetic", value: 21 }, { name: "Zeros", value: 37 } ]; function test(propOrders) { items.sort(function (a, b) { return SortByProps(a, b, propOrders); }); console.log(items); } function testAsc() { test({ "name": "asc", "value": "asc" }); } function testDesc() { test({ "name": "desc", "value": "desc" }); } function testAscDesc() { test({ "name": "asc", "value": "desc" }); } function testDescAsc() { test({ "name": "desc", "value": "asc" }); }實測效果
http://jsfiddle.net/Stronger/nktL5cwa/10
TypeScript代碼/** ** 排序方向。 */ type Direct = "asc" | "desc"; /** ** 排序屬性。 ** ** @interface IPropertyOrder */ interface IPropertyOrder { [name: string] : Direct; } /** ** 簡單名/值對象。 ** ** @interface ISimpleObject */ interface ISimpleObject { [name: string] : string | number | boolean; } /** ** 對簡單的名/值對象按照指定屬性和排序方向進行排序(根據排序屬性及排序方向, ** 對兩個項依次進行比較,并返回代表排序位置的值)。 ** ** @template T 簡單的名/值對象。 ** @param {T} item1 排序比較項1。 ** @param {T} item2 排序比較項2。 ** @param {...IPropertyOrder[]} props 排序屬性。 ** @returns 若項1大于項2返回1,若項1等于項2返回0,否則返回-1。 */ function SortByProps使用場景及局限性(item1: T, item2: T, ...props: IPropertyOrder[]) { "use strict"; var cps: Array = []; // 存儲排序屬性比較結果。 // 如果未指定排序屬性,則按照全屬性升序排序。 var asc = true; if (props.length < 1) { for (var p in item1) { if (item1[p] > item2[p]) { cps.push(1); break; // 大于時跳出循環。 } else if (item1[p] === item2[p]) { cps.push(0); } else { cps.push(-1); break; // 小于時跳出循環。 } } } else { // 按照指定屬性及升降方向進行排序。 for (var i = 0; i < props.length; i++) { var prop = props[i]; for (var o in prop) { asc = prop[o] === "asc"; if (item1[o] > item2[o]) { cps.push(asc ? 1 : -1); break; // 大于時跳出循環。 } else if (item1[o] === item2[o]) { cps.push(0); } else { cps.push(asc ? -1 : 1); break; // 小于時跳出循環。 } } } } for (var j = 0; j < cps.length; j++) { if (cps[j] === 1 || cps[j] === -1) { return cps[j]; } } return 0; }
在前端使用JavaScript實現多屬性排序,減少了對服務器端的請求,減輕服務器端的計算壓力,但是也僅適用于只需要對本地數據進行排序的情形。如果需要對整個數據集進行多屬性排序,最終還是要在服務器端的數據庫層面上進行。
如果你有更好的實現方式,歡迎留言交流。
本文章版權歸作者本人所有,轉載請注明出處。: )
參考資料JavaScript MDN - Array.prototype.sort : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/79669.html
摘要:從而將傳入的數組誤判為非數組。返回值把指定的值添加到數組后的新長度。方法用于刪除并返回數組的最后一個元素返回值的最后一個元素。如果數組已經為空,則不改變數組,并返回值。 JavaScript的array可以包含任意數據類型,并通過索引來訪問每個元素。 1、檢測數組:instanceof、slice()、Array.isArray() 檢測一個對象是不是數組的三種方法:(1)方法一:i...
摘要:事件的響應分區為三個階段捕獲目標冒泡階段。綁定的多個事件會被覆蓋,后者覆蓋前者。再用轉換成數值表示。如實際數量為,則展示為項目中使用過濾器做的處理可以抽取方法的,調整相關,可以獲取指定位數的縮寫。 CSS html5中a的download屬性 定義和用法download 屬性定義下載鏈接的地址或指定下載文件的名稱。文件名稱沒有限定值,瀏覽器會自動在文件名稱末尾添加該下載文件的后綴 (...
摘要:實現數組更多的高階函數吾輩的博客原文場景雖說人人平等,但有些人更加平等。若是有一篇適合萌新閱讀的自己實現數組更多操作的文章,情況或許會發生一些變化。類似于的初始值,但它是一個函數,避免初始值在所有分組中進行累加。 JavaScript 實現數組更多的高階函數 吾輩的博客原文: https://blog.rxliuli.com/p/fc... 場景 雖說人人平等,但有些人更加平等。 為...
摘要:操作符如何使用索引有一些查詢完全無法使用索引,也有一些查詢能夠比其他查詢更高效地使用索引。有時能夠使用索引,但是通常它并不知道要如何使用索引。索引對象和數組允許深入文檔內部,對嵌套字段和數組建立索引。 上一篇文章:MongoDB指南---10、索引、復合索引 簡介下一篇文章:MongoDB指南---12、使用explain()和hint()、何時不應該使用索引 1、使用復合索引 在多...
閱讀 3683·2021-11-16 11:41
閱讀 2890·2021-09-23 11:45
閱讀 695·2019-08-30 15:44
閱讀 548·2019-08-30 13:10
閱讀 1966·2019-08-30 12:49
閱讀 3534·2019-08-28 17:51
閱讀 1483·2019-08-26 12:20
閱讀 707·2019-08-23 17:56