120

How to implement the following scenario using Javascript only:

  • Create a car object with properties (top speed, brand, etc.)
  • Sort a list of cars ordered by those properties
Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Constructor
  • 1,225
  • 2
  • 9
  • 3
  • 3
    @durilai: JavaScript **is** object oriented, the OO model of JavaScript is based on *Prototyping* and is really, really versatile... http://en.wikipedia.org/wiki/Prototype-based_programming – Christian C. Salvadó Mar 17 '10 at 22:32
  • I recommend to use lodash.js : https://lodash.com/docs#sortBy – Chemical Programmer Jan 28 '16 at 17:46
  • The pattern for sorting by properties is `cars.sort((a, b) =>`…`)` with `a.prop` and `b.prop`. `a.prop - b.prop` sorts [numerically](/q/7889006/4642212), `a.prop.localeCompare(b.prop)` [lexicographically](/q/1129216/4642212), and `(b.prop < a.prop) - (a.prop < b.prop)` generically. To sort descending instead of ascending, negate the return value (e.g. `b.prop - a.prop` instead of `a.prop - b.prop`). To sort by [multiple properties](/q/6913512/4642212), chain other sorts with `||`, e.g. `b.someNumber - a.someNumber || a.someString.localeCompare(b.someString)`. – Sebastian Simon Apr 10 '22 at 11:57
  • Sorting by [array values at a specific index](/q/50415200/4642212) is exactly the same problem as sorting by object properties — the syntax just _looks_ different: e.g. `a[0] - b[0]`. And finally, if you’re looking to sort the properties of an object _themselves_, see [Sort JavaScript object by key](/q/5467129/4642212). – Sebastian Simon Apr 10 '22 at 11:57

7 Answers7

196

javascript has the sort function which can take another function as parameter - that second function is used to compare two elements.

Example:

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 

ok, from your comment i see that you're using the word 'sort' in a wrong sense. In programming "sort" means "put things in a certain order", not "arrange things in groups". The latter is much simpler - this is just how you "sort" things in the real world

  • make two empty arrays ("boxes")
  • for each object in your list, check if it matches the criteria
  • if yes, put it in the first "box"
  • if no, put it in the second "box"
user187291
  • 53,363
  • 19
  • 95
  • 127
  • 14
    Simple note for convenience: this (`a.someProp - b.someProp`) sorts from **lowest to highest**, and the reverse (`b.someProp - a.someProp`) sorts from highest to lowest. Basically, [if the function returns less than 0, a comes before b.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description) – user56reinstatemonica8 Aug 06 '13 at 12:27
  • Also note that this solution only works for when the properties you're sorting by are numbers. This works in the example for sorting by top speed, but if you want to sort by car brand, this solution won't sort strings alphabetically. [Cheeso provides an answer for sorting by both numbers and strings.](https://stackoverflow.com/a/2466503/1836703) – Cole Oct 30 '18 at 21:53
  • Also note that the sorting won't work when the numbers are very high or very low, i.e. when the subtraction operation underflows. – daniel kullmann Oct 27 '21 at 13:58
27

Example.

This runs on cscript.exe, on windows.

// 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");

You can also make a general sorter.

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");
Cheeso
  • 189,189
  • 101
  • 473
  • 713
18

I have wrote this simple function for myself:

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);
}

for example you have list of cars:

var cars= [{brand: 'audi', speed: 240}, {brand: 'fiat', speed: 190}];
var carsSortedByBrand = sortObj(cars, 'brand');
var carsSortedBySpeed = sortObj(cars, 'speed');
slavugan
  • 1,463
  • 17
  • 17
  • 1
    good but it seems that using `>` and `<` operands for value comparision is enough to handle both numeric and string properties (no `typeof ` check). – S.Serpooshan Apr 24 '22 at 17:06
14

Here's a short example, that creates and array of objects, and sorts numerically or alphabetically:

// 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);
theMaxx
  • 3,756
  • 2
  • 27
  • 33
  • It seems that using `>` and `<` operands for value comparision is enough to handle both `numeric` and `string` properties! so the second `sort` sample always works. Also, we may add some condition for equal case to micro-improve code performance to avoid un-neccessary repositioning: `(a, b) => (a.brand > b.brand) ? 1 : (a.brand < b.brand) ? -1 : 0` – S.Serpooshan Apr 24 '22 at 17:11
6

Let us say we have to sort a list of objects in ascending order based on a particular property, in this example lets say we have to sort based on the "name" property, then below is the required code :

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"}]
Jayanth G
  • 69
  • 1
  • 4
3

With ES6 arrow functions it will be like this:

//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() can accept a comparator function (here I used arrow notation, but ordinary functions work the same):

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 } ]

The above approach copies the contents of cars array into a new one and sorts it alphabetically based on brand names. Similarly, you can pass a different function:

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 } ]

If you don't mind mutating the orginal array cars.sort(comparatorFunction) will do the trick.

Farzad Vertigo
  • 2,458
  • 1
  • 29
  • 32
2

A version of Cheeso solution with reverse sorting, I also removed the ternary expressions for lack of clarity (but this is personal taste).

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;
  };
};
Marcs
  • 3,768
  • 5
  • 33
  • 42
  • 1
    To be fully reversed the numbers need `return !!reverse ? (a[prop] - b[prop]) * -1 : (a[prop] - b[prop]);` – Mark Schultheiss Mar 24 '17 at 14:29
  • Yes, as now no reverse check on numbers, thank you, I should fix that. But why the double `!` this is fine too: `return reverse ? (a[prop] - b[prop]) * -1 : (a[prop] - b[prop]);` – Marcs Mar 24 '17 at 15:06
  • 1
    The `!!` forces type coercion to a native boolean type value as opposed to the "falsy" nature of the JavaScript value, not strictly needed but clarifies purpose at least to me. Note that when you return a value with `!!` it is a native Boolean type as opposed to a native type with a "falsy" value which is to say the `typeof !!undefined` or `typeof !!null` etc. return "boolean" Note that `!!" "` is `true` but `!!""` is `false` (space, no space in the string) but you probably knew that already. – Mark Schultheiss Mar 24 '17 at 16:46