Javascript 迭代法实现数组多条件排序

多条件排序可能有很多种思路,效率也各不相同,我的方法可能只适合自己用,毕竟目的是为了实现功能,所以采用了最笨的方法,不过效果还是很理想的,经过多次测试,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-归并排序

posted @ 2016-08-30 18:22  绣春刀  阅读(2533)  评论(0编辑  收藏  举报