多條件排序可能有很多種思路,效率也各不相同,我的方法可能只適合自己用,畢竟目的是為了實現功能,所以采用了最笨的方法,不過效果還是很理想的,經過多次測試,6列1000行數據,平均排序時間大約是:28ms。
具體實現代碼如下:
function isArr(data) { return ({}).toString.call(data) == '[object Array]'; } function getIndex(arr) { var i = 0, len = arr.length keys = []; while (i < len) { keys.push(i++); } return keys; } // 檢測數組最大維數,非數組則返回-1,如果有num則表明檢測數組是否為指定維數 function checkArrDim(arr, num) { var dimension = -1, num = parseInt(num), isCheck = isNaN(num) ? false : true, dm = [0], i, len, mx; if (isArr(arr) && (len = arr.length) > 0) { dimension = 1; // 任何一個數組,只要有數據,至少是個1維 for (i = 0; i < len; i++) { dm.push(checkArrDim(arr[i])); // 遞歸獲取每個元素的維數,如果dm數組中全是-1則說明arr是1維數組 } dimension = (mx = Math.max.apply(null, dm)) === -1 ? dimension : dimension + mx; } // 如果dm數組長度 <= 1則說明arr壓根不是數組,或者是空數組 // 當dm數組長度 > 1,且dimension == 1,說明arr是1維數組 // 或者dimension <> 1,因為dm默認填充1個0,只要所有元素的和 / dm去掉0后的長度 == num - 1,即說明是n維數組 return isCheck ? (dm.length > 1 ? (dimension == 1 && num == 1) || eval(dm.join('+')) / (dm.length - 1) == num - 1 : false) : dimension; } function msort(arr, field, order) { if (!checkArrDim(arr, 2) || !checkArrDim(field, 1) || !checkArrDim(order, 1)) { return ; } var key, tmp, val, sa, sk, pre; var i, ilen, j, jlen, k, klen, m, mlen; var range = [], rng; // 按已排序數組的索引數組排序待定數組 var sortFromKey = function (data, key) { var tmp = [], i, j, len; for (i = 0, len = key.length; i < len; i++) { tmp.push(data[key[i]]); } for (j = 0; j < len; j++) { data[j] = tmp[j]; } }; // 多條件排序 for (i = 0, ilen = field.length; i < ilen; i++) { tmp = arr[field[i]]; if (i === 0) { // 第1次排序,直接對當前字段所在數組排序 key = getIndex(tmp); tmp.mergeSort(key, order[i]); range.push([0, tmp.length - 1]); } else { // 如果有第2個及以上的條件,則均以前1個條件為參照,獲取前1個已排序數組 // 內每一組相同元素的區間,對該區間內元素賦值到臨時數組並排序,並獲取排序 // 索引,最終拼接在一起這個拼接在一起的新的索引數組即是其它所有數組排序的參照, // 經過上述循環執行,即可完成多條件排序 // ↓核心工作前的初始工作 pre = arr[field[i - 1]]; // 前1個已排序數組 val = pre[0]; sa = [tmp[0]]; sk = [0]; key = []; rng = []; // 本排序核心工作即整理已排序數組的同值區間,此區間是當前待排序數組多個排序區間的唯一參照 for (k = 0, klen = range.length; k < klen; k++) { for (m = range[k][0] + 1, mlen = range[k][1] + 1; m <= mlen; m++) { // 注意此處條件表達式,需要額外執行一次排序和初始化 if (val === pre[m] && m !== mlen) { // 無論區間多小,哪怕只有1個元素,當m = mlen時必須執行排序和下一步的初始准備 sa.push(tmp[m]); sk.push(m); } else { rng = rng.concat([[sk[0], sk[sk.length - 1]]]); sa.mergeSort(sk, order[i]); // 主要是為了獲取sk key = key.concat(sk); val = pre[m]; sa = [tmp[m]]; sk = [m]; } } } range = rng; // 獲取整理后的待排序區間 sortFromKey(tmp, key); // 對當前數組排序 } // 經過前面的過程,一個條件已經排序完成,並且獲得排序后的原索引(數組) // 然后對除了當前字段數組外的其它所有數組按已排序索引重新排列 for (j = 0, jlen = arr.length; j < jlen; j++) { if (j == field[i]) { continue; } sortFromKey(arr[j], key); } } }
其中用到數組mergeSort()是自己定義到Array.prototype的方法,鏈接地址:Javascript-歸並排序