sort Javascript array by two numeric fields

JavascriptArraysSorting

Javascript Problem Overview


grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);      
    return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;
});

This code sorts the array by gsize, smallest to largest.

How would I change it to sort first by gsize and then by glow?

Javascript Solutions


Solution 1 - Javascript

grouperArray.sort(function (a, b) {   
    return a.gsize - b.gsize || a.glow - b.glow;
});

shorter version

Solution 2 - Javascript

grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);

    if(aSize == bSize)
    {
        return (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0;
    }
    else
    {
        return (aSize < bSize) ? -1 : 1;
    }
});

Solution 3 - Javascript

grouperArray.sort((a, b) => a.gsize - b.gsize || a.glow - b.glow);

Even shorter version using arrow syntax!

Solution 4 - Javascript

I realize this was asked some time ago, but I thought I would add my solution.

This function generates sort methods dynamically. simply supply each sortable child property name, prepended with +/- to indicate ascending or descending order. Super re-usable, and it doesn't need to know anything about the data structure you've put together. Could be made idiot proof - but doesn't seem necessary.

function getSortMethod(){
    var _args = Array.prototype.slice.call(arguments);
    return function(a, b){
        for(var x in _args){
            var ax = a[_args[x].substring(1)];
            var bx = b[_args[x].substring(1)];
            var cx;
            
            ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
            bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;
            
            if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
            if(ax != bx){return ax < bx ? -1 : 1;}
        }
    }
}

example usage:

items.sort(getSortMethod('-price', '+priority', '+name'));

this would sort items with lowest price first, with ties going to the item with the highest priority. further ties are broken by the item name

where items is an array like:

var items = [
    { name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
    { name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
    { name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
    { name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
    { name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 } ...
];

live demo: http://gregtaff.com/misc/multi_field_sort/

EDIT: Fixed issue with Chrome.

Solution 5 - Javascript

I expect the ternary operator ((aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;) has you confused. You should check out the link to understand it better.

Until then, here's your code blown out into full if/else.

grouperArray.sort(function (a, b) {
    if (a.gsize < b.gsize)
    {
        return -1;
    }
    else if (a.gsize > b.gsize)
    {
        return 1;
    }
    else
    {
        if (a.glow < b.glow)
        {
            return -1;
        }
        else if (a.glow > b.glow)
        {
            return 1;
        }
        return 0;
    }
});

Solution 6 - Javascript

Here's an implementation for those who may want something more generic that would work with any number of fields.

Array.prototype.sortBy = function (propertyName, sortDirection) {

    var sortArguments = arguments;
    this.sort(function (objA, objB) {

        var result = 0;
        for (var argIndex = 0; argIndex < sortArguments.length && result === 0; argIndex += 2) {

            var propertyName = sortArguments[argIndex];
            result = (objA[propertyName] < objB[propertyName]) ? -1 : (objA[propertyName] > objB[propertyName]) ? 1 : 0;
	    
            //Reverse if sort order is false (DESC)
            result *= !sortArguments[argIndex + 1] ? 1 : -1;
        }
        return result;
    });

}

Basically, you may specify any number of property name / sort direction:

var arr = [{
  LastName: "Doe",
  FirstName: "John",
  Age: 28
}, {
  LastName: "Doe",
  FirstName: "Jane",
  Age: 28
}, {
  LastName: "Foo",
  FirstName: "John",
  Age: 30
}];

arr.sortBy("LastName", true, "FirstName", true, "Age", false);
//Will return Jane Doe / John Doe / John Foo

arr.sortBy("Age", false, "LastName", true, "FirstName", false);
//Will return John Foo / John Doe / Jane Doe

Solution 7 - Javascript

grouperArray.sort(function (a, b) {
  var aSize = a.gsize;
  var bSize = b.gsize;
  var aLow = a.glow;
  var bLow = b.glow;
  console.log(aLow + " | " + bLow);      
  return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : ( (aLow < bLow ) ? -1 : (aLow > bLow ) ? 1 : 0 );
});

Solution 8 - Javascript

grouperArray.sort(function (a, b) {
     var aSize = a.gsize;     
     var bSize = b.gsize;     
     var aLow = a.glow;
     var bLow = b.glow;
     console.log(aLow + " | " + bLow);
     return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0); }); 

Solution 9 - Javascript

Here is an implementation that uses recursion to sort by any number of sort fields from 1 to infinite. You pass it a results array which is an array of result objects to sort, and a sorts array which is an array of sort objects defining the sort. Each sort object must have a "select" key for the key name that it sorts by and an "order" key which is a string indicating "ascending" or "descending".

sortMultiCompare = (a, b, sorts) => {
    let select = sorts[0].select
    let order = sorts[0].order
    if (a[select] < b[select]) {
        return order == 'ascending' ? -1 : 1
    } 
    if (a[select] > b[select]) {
        return order == 'ascending' ? 1 : -1
    }
    if(sorts.length > 1) {
        let remainingSorts = sorts.slice(1)
        return this.sortMultiCompare(a, b, remainingSorts)
    }
    return 0
}

sortResults = (results, sorts) => {
    return results.sort((a, b) => {
        return this.sortMultiCompare(a, b, sorts)
    })
}

// example inputs
const results = [
    {
        "LastName": "Doe",
        "FirstName": "John",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Doe",
        "FirstName": "Jane",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Johnson",
        "FirstName": "Kevin",
        "MiddleName": "Bill"
    }
]

const sorts = [
    {
        "select": "LastName",
        "order": "ascending"
    },
    {
        "select": "FirstName",
        "order": "ascending"
    },
    {
        "select": "MiddleName",
        "order": "ascending"
    }    
]

// call the function like this:
let sortedResults = sortResults(results, sorts)

Solution 10 - Javascript

A dynamic way to do that with MULTIPLE keys:

  • filter unique values from each col/key of sort
  • put in order or reverse it
  • add weights width zeropad for each object based on indexOf(value) keys values
  • sort using caclutated weights

enter image description here

Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) { 
    sorts.map(sort => {            
        sort.uniques = Array.from(
            new Set(this.map(obj => obj[sort.key]))
        );
        
        sort.uniques = sort.uniques.sort((a, b) => {
            if (typeof a == 'string') {
                return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
            }
            else if (typeof a == 'number') {
                return sort.inverse ? (a < b) : (a > b ? 1 : 0);
            }
            else if (typeof a == 'boolean') {
                let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                return x;
            }
            return 0;
        });
    });

    const weightOfObject = (obj) => {
        let weight = "";
        sorts.map(sort => {
            let zeropad = `${sort.uniques.length}`.length;
            weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
        });
        //obj.weight = weight; // if you need to see weights
        return weight;
    }

    this.sort((a, b) => {
        return weightOfObject(a).localeCompare( weightOfObject(b) );
    });
    
    return this;
}
});

Use:

// works with string, number and boolean
let sortered = your_array.orderBy([
    {key: "type", inverse: false}, 
    {key: "title", inverse: false},
    {key: "spot", inverse: false},
    {key: "internal", inverse: true}
]);

enter image description here

Solution 11 - Javascript

This is what I use

function sort(a, b) {
    var _a = "".concat(a.size, a.glow);
    var _b = "".concat(b.size, b.glow);
    return _a < _b;
}

concat the two items as a string and they will be sorted by a string value. If you want you could wrap _a and _b with parseInt to compare them as numbers if you know they will be numerical.

Solution 12 - Javascript

Here is the solution for the case, when you have a priority sort key, which might not exist in some particular items, so you have to sort by fallback keys.

An input data example (id2 is priority sort key):

const arr = [
    {id: 1},
    {id: 2, id2: 3},
    {id: 4},
    {id: 3},
    {id: 10, id2: 2},
    {id: 7},
    {id: 6, id2: 1},
    {id: 5},
    {id: 9, id2: 2},
    {id: 8},
];

And the output should be:

[ { id: 6, id2: 1 },
  { id: 9, id2: 2 },
  { id: 10, id2: 2 },
  { id: 2, id2: 3 },
  { id: 1 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 7 },
  { id: 8 } ]

The comparator function will be like:

arr.sort((a,b) => {
  if(a.id2 || b.id2) {
    if(a.id2 && b.id2) {
      if(a.id2 === b.id2) {
        return a.id - b.id;
      }
      return a.id2 - b.id2;
    }
    return a.id2 ? -1 : 1;
  }
  return a.id - b.id
});

P.S. In case if .id of .id2 can be zeros, consider to use typeof.

Solution 13 - Javascript

Let's simplify.

Say you have an array of arrays:

let tmp = [
    [0, 1],
    [2, 1],
    [1, 1],
    [0, 0],
    [2, 0],
    [1, 0],
    [0, 2],
    [2, 2],
    [1, 2],
]

Executing:

tmp.sort((a, b) => {
    if (a[1] != b[1])
        return a[1] - b[1];
    else
        return a[0] - b[0];
})

Will yield:

[
    [0, 0],
    [1, 0],
    [2, 0],
    [0, 1],
    [1, 1],
    [2, 1],
    [0, 2],
    [1, 2],
    [2, 2]
]

Solution 14 - Javascript

grouperArray.sort(function (a, b) {
	var aSize = a.gsize;
	var bSize = b.gsize;
	if (aSize !== aSize)
		return aSize - bSize;
	return a.glow - b.glow;
});

not tested, but I think that should work.

Solution 15 - Javascript

grouperArray.sort(
  function(a,b){return a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize}
);

Solution 16 - Javascript

In my case, i sort notification list by param 'important' and by 'date'

  • step 1: i filter notifications by 'important' and unImportant

    let importantNotifications = notifications.filter(
            (notification) => notification.isImportant);
          
      let unImportantNotifications = notifications.filter(
            (notification) => !notification.isImportant);
    
  • step 2: i sort them by date

      sortByDate = (notifications) => {
      return notifications.sort((notificationOne, notificationTwo) => {
        return notificationOne.date - notificationTwo.date;
      });
    };
    
  • step 3: merge them

    [
        ...this.sortByDate(importantNotifications),
        ...this.sortByDate(unImportantNotifications),
      ];
    

Solution 17 - Javascript

If you're happy to use the new tidy.js package you can achieve this with

tidy(input_array,
  arrange(['var1', desc('var2')])
);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMarkView Question on Stackoverflow
Solution 1 - Javascriptanmorozov23View Answer on Stackoverflow
Solution 2 - JavascriptChris EberleView Answer on Stackoverflow
Solution 3 - JavascriptVinorthView Answer on Stackoverflow
Solution 4 - JavascriptnihltonView Answer on Stackoverflow
Solution 5 - JavascriptJohn GreenView Answer on Stackoverflow
Solution 6 - JavascriptThe_Black_SmurfView Answer on Stackoverflow
Solution 7 - JavascriptsilexView Answer on Stackoverflow
Solution 8 - JavascriptTim WilliamsView Answer on Stackoverflow
Solution 9 - JavascriptBenjamin PortmanView Answer on Stackoverflow
Solution 10 - JavascriptLeonardo FilipeView Answer on Stackoverflow
Solution 11 - JavascriptblockloopView Answer on Stackoverflow
Solution 12 - JavascriptDenis LView Answer on Stackoverflow
Solution 13 - JavascriptUlf AslakView Answer on Stackoverflow
Solution 14 - JavascriptXanderView Answer on Stackoverflow
Solution 15 - Javascriptic3b3rgView Answer on Stackoverflow
Solution 16 - JavascriptPhạm HùngView Answer on Stackoverflow
Solution 17 - JavascriptgeotheoryView Answer on Stackoverflow