从这个最初的问题,我将如何在多个字段应用排序?

使用这种稍作调整的结构,我将如何排序城市(上升)和价格(下降)?

var homes = [
    {"h_id":"3",
     "city":"Dallas",
     "state":"TX",
     "zip":"75201",
     "price":"162500"},
    {"h_id":"4",
     "city":"Bevery Hills",
     "state":"CA",
     "zip":"90210",
     "price":"319250"},
    {"h_id":"6",
     "city":"Dallas",
     "state":"TX",
     "zip":"75000",
     "price":"556699"},
    {"h_id":"5",
     "city":"New York",
     "state":"NY",
     "zip":"00010",
     "price":"962500"}
    ];

我喜欢的事实是,给出的答案提供了一个一般的方法。在我计划使用这段代码的地方,我将不得不对日期以及其他东西进行排序。“启动”对象的能力似乎很方便,如果不是有点麻烦的话。

我试图把这个答案构建成一个很好的通用示例,但我运气不太好。


当前回答

这是一个通用的多维排序,允许在每个层次上进行反转和/或映射。

用Typescript编写。对于Javascript,请查看这个JSFiddle

的代码

type itemMap = (n: any) => any;

interface SortConfig<T> {
  key: keyof T;
  reverse?: boolean;
  map?: itemMap;
}

export function byObjectValues<T extends object>(keys: ((keyof T) | SortConfig<T>)[]): (a: T, b: T) => 0 | 1 | -1 {
  return function(a: T, b: T) {
    const firstKey: keyof T | SortConfig<T> = keys[0];
    const isSimple = typeof firstKey === 'string';
    const key: keyof T = isSimple ? (firstKey as keyof T) : (firstKey as SortConfig<T>).key;
    const reverse: boolean = isSimple ? false : !!(firstKey as SortConfig<T>).reverse;
    const map: itemMap | null = isSimple ? null : (firstKey as SortConfig<T>).map || null;

    const valA = map ? map(a[key]) : a[key];
    const valB = map ? map(b[key]) : b[key];
    if (valA === valB) {
      if (keys.length === 1) {
        return 0;
      }
      return byObjectValues<T>(keys.slice(1))(a, b);
    }
    if (reverse) {
      return valA > valB ? -1 : 1;
    }
    return valA > valB ? 1 : -1;
  };
}

用法示例

先按姓排序,再按名排序:

interface Person {
  firstName: string;
  lastName: string;
}

people.sort(byObjectValues<Person>(['lastName','firstName']));

按语言代码的名称排序,而不是按语言代码排序(见地图),然后按降序排序(见反向)。

interface Language {
  code: string;
  version: number;
}

// languageCodeToName(code) is defined elsewhere in code

languageCodes.sort(byObjectValues<Language>([
  {
    key: 'code',
    map(code:string) => languageCodeToName(code),
  },
  {
    key: 'version',
    reverse: true,
  }
]));

其他回答

function sort(data, orderBy) {
        orderBy = Array.isArray(orderBy) ? orderBy : [orderBy];
        return data.sort((a, b) => {
            for (let i = 0, size = orderBy.length; i < size; i++) {
                const key = Object.keys(orderBy[i])[0],
                    o = orderBy[i][key],
                    valueA = a[key],
                    valueB = b[key];
                if (!(valueA || valueB)) {
                    console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
                    return [];
                }
                if (+valueA === +valueA) {
                    return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
                } else {
                    if (valueA.localeCompare(valueB) > 0) {
                        return o.toLowerCase() === 'desc' ? -1 : 1;
                    } else if (valueA.localeCompare(valueB) < 0) {
                        return o.toLowerCase() === 'desc' ? 1 : -1;
                    }
                }
            }
        });
    }

使用:

sort(homes, [{city : 'asc'}, {price: 'desc'}])

var homes = [ {"h_id":"3", "city":"Dallas", "state":"TX", "zip":"75201", "price":"162500"}, {"h_id":"4", "city":"Bevery Hills", "state":"CA", "zip":"90210", "price":"319250"}, {"h_id":"6", "city":"Dallas", "state":"TX", "zip":"75000", "price":"556699"}, {"h_id":"5", "city":"New York", "state":"NY", "zip":"00010", "price":"962500"} ]; function sort(data, orderBy) { orderBy = Array.isArray(orderBy) ? orderBy : [orderBy]; return data.sort((a, b) => { for (let i = 0, size = orderBy.length; i < size; i++) { const key = Object.keys(orderBy[i])[0], o = orderBy[i][key], valueA = a[key], valueB = b[key]; if (!(valueA || valueB)) { console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!"); return []; } if (+valueA === +valueA) { return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB; } else { if (valueA.localeCompare(valueB) > 0) { return o.toLowerCase() === 'desc' ? -1 : 1; } else if (valueA.localeCompare(valueB) < 0) { return o.toLowerCase() === 'desc' ? 1 : -1; } } } }); } console.log(sort(homes, [{city : 'asc'}, {price: 'desc'}]));

我一直在寻找类似的东西,最后得到了这个:

首先,我们有一个或多个排序函数,总是返回0、1或-1:

const sortByTitle = (a, b): number => 
  a.title === b.title ? 0 : a.title > b.title ? 1 : -1;

您可以为想要排序的其他属性创建更多函数。

然后我有一个函数将这些排序函数合并为一个:

const createSorter = (...sorters) => (a, b) =>
  sorters.reduce(
    (d, fn) => (d === 0 ? fn(a, b) : d),
    0
  );

这可以用来以一种可读的方式组合上述排序函数:

const sorter = createSorter(sortByTitle, sortByYear)

items.sort(sorter)

当一个排序函数返回0时,将调用下一个排序函数进行进一步排序。

另一种方式

var homes = [ {"h_id":"3", "city":"Dallas", "state":"TX", "zip":"75201", "price":"162500"}, {"h_id":"4", "city":"Bevery Hills", "state":"CA", "zip":"90210", "price":"319250"}, {"h_id":"6", "city":"Dallas", "state":"TX", "zip":"75000", "price":"556699"}, {"h_id":"5", "city":"New York", "state":"NY", "zip":"00010", "price":"962500"} ]; function sortBy(ar) { return ar.sort((a, b) => a.city === b.city ? b.price.toString().localeCompare(a.price) : a.city.toString().localeCompare(b.city)); } console.log(sortBy(homes));

function sortMultiFields(prop){
    return function(a,b){
        for(i=0;i<prop.length;i++)
        {
            var reg = /^\d+$/;
            var x=1;
            var field1=prop[i];
            if(prop[i].indexOf("-")==0)
            {
                field1=prop[i].substr(1,prop[i].length);
                x=-x;
            }

            if(reg.test(a[field1]))
            {
                a[field1]=parseFloat(a[field1]);
                b[field1]=parseFloat(b[field1]);
            }
            if( a[field1] > b[field1])
                return x;
            else if(a[field1] < b[field1])
                return -x;
        }
    }
}

如果你想按降序排序特定字段,如何使用(在字段前放-(减号)号)

homes.sort(sortMultiFields(["city","-price"]));

使用上面的函数,你可以对带有多个字段的json数组进行排序。根本不需要改变函数体

改编自@chriskelly的回答。


大多数答案都忽略了,如果价值在1万美元以下或超过100万美元,价格将无法正确排序。原因是JS按字母顺序排序。这里回答得很好,为什么JavaScript不能对“5,10,1”排序,这里如何正确地对整数数组排序。

最后,如果我们要排序的字段或节点是一个数字,我们必须做一些计算。我并不是说在这种情况下使用parseInt()是正确的答案,排序结果更重要。

var homes = [{ "h_id": "2", "city": "Dallas", "state": "TX", "zip": "75201", "price": "62500" }, { "h_id": "1", "city": "Dallas", "state": "TX", "zip": "75201", "price": "62510" }, { "h_id": "3", "city": "Dallas", "state": "TX", "zip": "75201", "price": "162500" }, { "h_id": "4", "city": "Bevery Hills", "state": "CA", "zip": "90210", "price": "319250" }, { "h_id": "6", "city": "Dallas", "state": "TX", "zip": "75000", "price": "556699" }, { "h_id": "5", "city": "New York", "state": "NY", "zip": "00010", "price": "962500" }]; homes.sort(fieldSorter(['price'])); // homes.sort(fieldSorter(['zip', '-state', 'price'])); // alternative function fieldSorter(fields) { return function(a, b) { return fields .map(function(o) { var dir = 1; if (o[0] === '-') { dir = -1; o = o.substring(1); } if (!parseInt(a[o]) && !parseInt(b[o])) { if (a[o] > b[o]) return dir; if (a[o] < b[o]) return -(dir); return 0; } else { return dir > 0 ? a[o] - b[o] : b[o] - a[o]; } }) .reduce(function firstNonZeroValue(p, n) { return p ? p : n; }, 0); }; } document.getElementById("output").innerHTML = '<pre>' + JSON.stringify(homes, null, '\t') + '</pre>'; <div id="output"> </div>


用来测试的小提琴