对对象数组进行多次排序

101 阅读3分钟

该函数可以对数组arr进行排序。排序的依据是通过函数参数(从第二个参数开始)指定的多个排序条件。这些条件可以是对象、函数或字符串。

function sort(arr) {
    // 收集排序条件
    var sortConditions = [];
    for (var i = 1; i < arguments.length; i++) {
        sortConditions.push(arguments[i]);
    }

    // 自定义排序函数
    arr.sort(function (a, b) {
        if (a === null) return -1; // 如果 a 是 null,a 排在前面
        if (b === null) return 1; // 如果 b 是 null,b 排在后面

        // 遍历所有排序条件
        for (var j = 0; j < sortConditions.length; j++) {
            var condition = sortConditions[j];
            var sortDirection = 1; // 默认升序

            // 检查条件类型
            if (typeof condition === 'object') {
                // 如果条件是对象,检查其 type 属性
                if (typeof condition.type === 'number') {
                    sortDirection = condition.type; // 使用给定的排序方向
                } else if (condition.type && condition.type.toLowerCase() === 'desc') {
                    sortDirection = -1; // 降序
                }
                // 使用对象的 name 属性作为排序键
                condition = condition.name;
            } else if (typeof condition === 'function') {
                // 如果条件是函数,直接调用并返回结果
                var result = condition(a, b);
                if (result !== 0) {
                    return result * sortDirection; // 应用排序方向
                }
                // 如果函数返回 0,继续下一个条件
                continue;
            } else if (typeof condition === 'string') {
                // 如果条件是字符串,检查是否包含方向指示
                var parts = condition.split(':');
                condition = parts[0]; // 排序键
                if (parts[1] && parts[1].toLowerCase() === 'desc') {
                    sortDirection = -1; // 降序
                }
            }

            // 如果 a 和 b 都是对象,则按条件指定的属性进行比较
            if (typeof a === 'object' && typeof b === 'object') {
                var keys = condition.split('.');
                var aValue = a;
                var bValue = b;
                for (var k = 0; k < keys.length; k++) {
                    if (aValue && aValue.hasOwnProperty(keys[k])) {
                        aValue = aValue[keys[k]];
                    } else {
                        aValue = undefined; // 如果属性不存在,设为 undefined
                    }
                    if (bValue && bValue.hasOwnProperty(keys[k])) {
                        bValue = bValue[keys[k]];
                    } else {
                        bValue = undefined; // 如果属性不存在,设为 undefined
                    }
                }
                // 比较属性值
                if (aValue < bValue) return -sortDirection; // a 应该排在前面
                if (aValue > bValue) return sortDirection; // b 应该排在前面
            } else {
                // 如果 a 和 b 不是对象,则直接比较它们
                if (a < b) return -sortDirection; // a 应该排在前面
                if (a > b) return sortDirection; // b 应该排在前面
            }
        }

        // 所有条件都相同,返回 0
        return 0;
    });

    // 返回排序后的数组
    return arr;
}

原数组:

var a=[{name:"xpp", lvl:90, power:20, age:15},{name:"xtpp", lvl:9, power:25, age:15},{name:"tpp", lvl:50, power:18, age:10},{name:"tpp", lvl:50, power:38, age:10},{name:"tpp", lvl:9, power:20, age:10}]

对数组a,先按等级降序排列,再按战力升序排列

方法一:

sort(a, "lvl:desc","power:asc")

方法二:

sort(a, {name:"lvl", type:"desc"},{name:"power", type:"asc"})

排序列结果:

[{"name":"xpp","lvl":90,"power":20,"age":15},{"name":"tpp","lvl":50,"power":18,"age":10},{"name":"tpp","lvl":50,"power":38,"age":10},{"name":"tpp","lvl":9,"power":20,"age":10},{"name":"xtpp","lvl":9,"power":25,"age":15}]
/**
     * 对数组进行排序的通用方法,支持多字段排序。
     * 
     * @param arr 要排序的数组。
     * @param sorters 排序规则,可以是函数、字符串(格式为 "字段名:desc" 表示降序)、或带有字段和排序类型的对象。
     *                例如:sorters: ['age', { name: 'name', type: 'desc' },“power:asc”] 表示先按 age 升序,再按 name 降序排序,最后按power升序
     * 
     * @returns 排序后的新数组。
     */
    sort<T>(arr: T[], ...sorters: Array<((a: T, b: T) => number) | string | { name: string, type?: number | 'desc' }>): T[] {
        return arr.sort((a: T, b: T): number => {
            // 空值处理:null 始终排在最后
            if (a === null) return -1;
            if (b === null) return 1;

            for (const sorter of sorters) {
                let compareValue = 1; // 默认升序
                let fieldPath: string;

                // 如果是自定义比较函数
                if (typeof sorter === 'function') {
                    const result = sorter(a, b);
                    if (result !== 0) return result; // 比较结果不为0则直接返回
                    continue; // 否则继续下一个排序条件
                }

                // 处理对象类型的排序器 { name: '字段名', type: 'desc' }
                if (typeof sorter === 'object' && sorter !== null) {
                    if (typeof sorter.type === 'number') {
                        compareValue = sorter.type; // 自定义数值类型
                    } else if (sorter.type?.toLowerCase() === 'desc') {
                        compareValue = -1; // 降序排列
                    }
                    fieldPath = sorter.name as string;
                }
                // 处理字符串类型的排序器,如 "字段名:desc"
                else if (typeof sorter === 'string') {
                    const parts = sorter.split(':');
                    fieldPath = parts[0];

                    if (parts.length > 1 && parts[1].toLowerCase() === 'desc') {
                        compareValue = -1; // 降序排列
                    }
                }

                // 获取嵌套属性值,例如 a.field.subField
                const getNestedValue = (obj: any, path: string): any => {
                    return path.split('.').reduce((current, key) => current?.[key], obj);
                };

                const aVal = getNestedValue(a, fieldPath);
                const bVal = getNestedValue(b, fieldPath);

                // 实际比较逻辑
                if (aVal < bVal) return compareValue <= 0 ? 1 : -1; // 升序或降序
                if (aVal > bVal) return compareValue <= 0 ? -1 : 1;
            }

            return 0; // 所有排序字段相等时返回0
        });
    }

上面的这个要测试