按属性值对对象进行排序

121

如何仅使用Javascript实现以下场景:

  • 创建一个带有属性(最高速度、品牌等)的汽车对象
  • 按照这些属性对汽车列表进行排序

3
JavaScript是面向对象的,它的面向对象模型基于原型(Prototyping)并且非常灵活。参考链接:http://en.wikipedia.org/wiki/Prototype-based_programming - Christian C. Salvadó
我建议使用lodash.js:https://lodash.com/docs#sortBy - Chemical Programmer
按属性排序的模式为cars.sort((a, b) =>),其中a.propb.prop分别表示两个待排序对象的属性。a.prop - b.prop用于数字排序,a.prop.localeCompare(b.prop)用于字典序排序,(b.prop < a.prop) - (a.prop < b.prop)用于通用排序。如果要降序排列而不是升序,则取相反数(例如,使用b.prop - a.prop而不是a.prop - b.prop)。要按多个属性排序,请使用||链接其他排序方法,例如:b.someNumber - a.someNumber || a.someString.localeCompare(b.someString) - Sebastian Simon
特定索引处的数组值排序与按对象属性排序完全相同 - 语法只是_看起来_不同:例如 a[0] - b[0]。最后,如果您想对对象的属性本身进行排序,请参见按键排序JavaScript对象 - Sebastian Simon
7个回答

199

JavaScript有一个名为sort的函数,该函数可以使用另一个函数作为参数——这个第二个函数用于比较两个元素。

例如:

cars = [

    {
        name: "Honda",
        speed: 80
    },

    {
        name: "BMW",
        speed: 180
    },

    {
        name: "Trabi",
        speed: 40
    },

    {
        name: "Ferrari",
        speed: 200
    }
]


cars.sort(function(a, b) { 
    return a.speed - b.speed;
})

for(var i in cars)
    document.writeln(cars[i].name) // Trabi Honda BMW Ferrari 

好的,从您的评论中我看到您错误地使用了“sort”这个词。在编程中,“sort”表示“按照一定的顺序排列物品”,而不是“将物品分组排列”。后者要简单得多 - 这就是你在现实世界中“排序”物品的方法

  • 创建两个空数组("箱子")
  • 对于列表中的每个对象,检查它是否符合条件
  • 如果是,请将其放入第一个“箱子”
  • 如果不是,请将其放入第二个“箱子”

14
为了方便起见,请注意:a.someProp - b.someProp 表示从最低到最高排序,而反过来的表达式 b.someProp - a.someProp 表示从最高到最低排序。基本上,如果函数返回小于0的值,则a会排在b之前。 - user56reinstatemonica8
请注意,此解决方案仅适用于您要排序的属性为数字的情况。这在按最高速度排序的示例中有效,但如果您想按汽车品牌排序,则此解决方案不会按字母顺序排序字符串。Cheeso提供了按数字和字符串排序的答案。 - Cole
还要注意,当数字非常高或非常低时,即减法操作下溢时,排序将无法工作。 - daniel kullmann

27

示例。

这在Windows上使用cscript.exe运行。

// define the Car class
(function() {
    // makeClass - By John Resig (MIT Licensed)
    // Allows either new User() or User() to be employed for construction.
    function makeClass(){
        return function(args){
            if ( this instanceof arguments.callee ) {
                if ( typeof this.init == "function" )
                    this.init.apply( this, (args && args.callee) ? args : arguments );
            } else
                return new arguments.callee( arguments );
        };
    }

    Car = makeClass();

    Car.prototype.init = function(make, model, price, topSpeed, weight) {
        this.make = make;
        this.model = model;
        this.price = price;
        this.weight = weight;
        this.topSpeed = topSpeed;
    };
})();


// create a list of cars
var autos = [
    new Car("Chevy", "Corvair", 1800, 88, 2900),
    new Car("Buick", "LeSabre", 31000, 138, 3700),
    new Car("Toyota", "Prius", 24000, 103, 3200),
    new Car("Porsche", "911", 92000, 155, 3100),
    new Car("Mercedes", "E500", 67000, 145, 3800),
    new Car("VW", "Passat", 31000, 135, 3700)
];

// a list of sorting functions
var sorters = {
    byWeight : function(a,b) {
        return (a.weight - b.weight);
    },
    bySpeed : function(a,b) {
        return (a.topSpeed - b.topSpeed);
    },
    byPrice : function(a,b) {
        return (a.price - b.price);
    },
    byModelName : function(a,b) {
        return ((a.model < b.model) ? -1 : ((a.model > b.model) ? 1 : 0));
    },
    byMake : function(a,b) {
        return ((a.make < b.make) ? -1 : ((a.make > b.make) ? 1 : 0));
    }
};

function say(s) {WScript.Echo(s);}

function show(title)
{
    say ("sorted by: "+title);
    for (var i=0; i < autos.length; i++) {
        say("  " + autos[i].model);
    }
    say(" ");
}

autos.sort(sorters.byWeight);
show("Weight");

autos.sort(sorters.byModelName);
show("Name");

autos.sort(sorters.byPrice);
show("Price");
你也可以制作一个通用的排序器。
var byProperty = function(prop) {
    return function(a,b) {
        if (typeof a[prop] == "number") {
            return (a[prop] - b[prop]);
        } else {
            return ((a[prop] < b[prop]) ? -1 : ((a[prop] > b[prop]) ? 1 : 0));
        }
    };
};

autos.sort(byProperty("topSpeed"));
show("Top Speed");

18

我为自己写了这个简单的函数:

function sortObj(list, key) {
    function compare(a, b) {
        a = a[key];
        b = b[key];
        var type = (typeof(a) === 'string' ||
                    typeof(b) === 'string') ? 'string' : 'number';
        var result;
        if (type === 'string') result = a.localeCompare(b);
        else result = a - b;
        return result;
    }
    return list.sort(compare);
}

举个例子,你有一份车辆清单:

var cars= [{brand: 'audi', speed: 240}, {brand: 'fiat', speed: 190}];
var carsSortedByBrand = sortObj(cars, 'brand');
var carsSortedBySpeed = sortObj(cars, 'speed');

1
很好,但似乎使用><操作符进行值比较就足以处理数字和字符串属性(无需typeof检查)。 - S.Serpooshan

16

这是一个简短的示例,创建了一个对象数组,并按数字或字母顺序排序:

// Create Objects Array

var arrayCarObjects = [
{brand: "Honda",        topSpeed: 45},
{brand: "Ford",         topSpeed: 6},
{brand: "Toyota",       topSpeed: 240},
{brand: "Chevrolet",    topSpeed: 120},
{brand: "Ferrari",      topSpeed: 1000}
];

// Sort Objects Numerically

arrayCarObjects.sort((a, b) => (a.topSpeed - b.topSpeed));

// Sort Objects Alphabetically

arrayCarObjects.sort((a, b) => (a.brand > b.brand) ? 1 : -1);

似乎使用><运算符比较值就足以处理数字和字符串属性了!所以第二个sort示例总是有效的。此外,我们可以添加一些等于情况的条件来微调代码性能,以避免不必要的重新定位:(a,b) =>(a.brand> b.brand)?1:(a.brand <b.brand)?-1:0 - S.Serpooshan

6

假设我们需要根据特定属性按升序对对象列表进行排序,在本例中,我们要按“名称”属性排序,那么以下是所需的代码:

var list_Objects = [{"name"="Bob"},{"name"="Jay"},{"name"="Abhi"}];
Console.log(list_Objects);   //[{"name"="Bob"},{"name"="Jay"},{"name"="Abhi"}]
    list_Objects.sort(function(a,b){
        return a["name"].localeCompare(b["name"]); 
    });
Console.log(list_Objects);  //[{"name"="Abhi"},{"name"="Bob"},{"name"="Jay"}]

3

使用ES6箭头函数,代码将变为以下形式:

//Let's say we have these cars
let cars = [ { brand: 'Porsche', top_speed: 260 },
  { brand: 'Benz', top_speed: 110 },
  { brand: 'Fiat', top_speed: 90 },
  { brand: 'Aston Martin', top_speed: 70 } ]

Array.prototype.sort()可以接受一个比较函数(这里我使用了箭头符号,但普通函数的效果相同):

let sortedByBrand = [...cars].sort((first, second) => first.brand > second.brand)

// [ { brand: 'Aston Martin', top_speed: 70 },
//   { brand: 'Benz', top_speed: 110 },
//   { brand: 'Fiat', top_speed: 90 },
//   { brand: 'Porsche', top_speed: 260 } ]

上述方法将cars数组的内容复制到一个新的数组中,并根据品牌名称按字母顺序进行排序。同样,您也可以传递不同的函数:
let sortedBySpeed =[...cars].sort((first, second) => first.top_speed > second.top_speed)

//[ { brand: 'Aston Martin', top_speed: 70 },
//  { brand: 'Fiat', top_speed: 90 },
//  { brand: 'Benz', top_speed: 110 },
//  { brand: 'Porsche', top_speed: 260 } ]

如果你不介意改变原始数组,cars.sort(comparatorFunction)可以解决问题。


2

这是一个带有逆向排序的Cheeso解决方案版本,我还移除了三目运算表达式以避免不清晰(但这是个人口味)。

function(prop, reverse) {
  return function(a, b) {
    if (typeof a[prop] === 'number') {
      return (a[prop] - b[prop]);
    }

    if (a[prop] < b[prop]) {
      return reverse ? 1 : -1;
    }

    if (a[prop] > b[prop]) {
      return reverse ? -1 : 1;
    }

    return 0;
  };
};

1
为了完全反转数字,需要 return !!reverse ? (a[prop] - b[prop]) * -1 : (a[prop] - b[prop]); - Mark Schultheiss
是的,因为现在没有对数字进行反向检查,谢谢,我应该修复它。但是为什么要使用双重 !?这也可以:return reverse ? (a[prop] - b[prop]) * -1 : (a[prop] - b[prop]); - Marcs
1
!! 强制类型转换为本地布尔类型值,而不是 JavaScript 值的“假”性质,虽然不是严格必需的,但至少对我来说可以澄清目的。请注意,当您使用 !! 返回一个值时,它是本地布尔类型,而不是具有“假”值的本地类型,也就是说,typeof !!undefinedtypeof !!null 等返回 "boolean"。请注意,!!" "true,但 !!""false(字符串中有空格和没有空格),但您可能已经知道了这一点。 - Mark Schultheiss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接