Sort array of objects by single key with date value

JavascriptArraysObject

Javascript Problem Overview


I have an array of objects with several key value pairs, and I need to sort them based on 'updated_at':

[    {        "updated_at" : "2012-01-01T06:25:24Z",        "foo" : "bar"    },    {        "updated_at" : "2012-01-09T11:25:13Z",        "foo" : "bar"    },    {        "updated_at" : "2012-01-05T04:13:24Z",        "foo" : "bar"    }]

What's the most efficient way to do so?

Javascript Solutions


Solution 1 - Javascript

You can use Array.sort.

Here's an example:

var arr = [{
    "updated_at": "2012-01-01T06:25:24Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-09T11:25:13Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-05T04:13:24Z",
    "foo": "bar"
  }
]

arr.sort(function(a, b) {
  var keyA = new Date(a.updated_at),
    keyB = new Date(b.updated_at);
  // Compare the 2 dates
  if (keyA < keyB) return -1;
  if (keyA > keyB) return 1;
  return 0;
});

console.log(arr);

Solution 2 - Javascript

I already answered a really similar question here: https://stackoverflow.com/questions/8175093/simple-function-to-sort-a-json-object-using-javascript/8175221#8175221

For that question I created this little function that might do what you want:

function sortByKey(array, key) {
    return array.sort(function(a, b) {
        var x = a[key]; var y = b[key];
        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
}

Solution 3 - Javascript

The Array.sort() method sorts the elements of an array in place and returns the array. Be careful with Array.sort() as it's not Immutable. For immutable sort use immutable-sort.

This method is to sort the array using your current updated_at in ISO format. We use new Data(iso_string).getTime() to convert ISO time to Unix timestamp. A Unix timestamp is a number that we can do simple math on. We subtract the first and second timestamp the result is; if the first timestamp is bigger than the second the return number will be positive. If the second number is bigger than the first the return value will be negative. If the two are the same the return will be zero. This alines perfectly with the required return values for the inline function.

For ES6:

arr.sort((a,b) => new Date(a.updated_at).getTime() - new Date(b.updated_at).getTime());

For ES5:

arr.sort(function(a,b){ 
 return new Date(a.updated_at).getTime() - new Date(b.updated_at).getTime();
});

If you change your updated_at to be unix timestamps you can do this:

For ES6:

arr.sort((a,b) => a.updated_at - b.updated_at);

For ES5:

arr.sort(function(a,b){ 
 return a.updated_at - b.updated_at;
});

At the time of this post, modern browsers do not support ES6. To use ES6 in modern browsers use babel to transpile the code to ES5. Expect browser support for ES6 in the near future.

Array.sort() should receave a return value of one of 3 possible outcomes:

  • A positive number (first item > second item)
  • A negative number (first item < second item)
  • 0 if the two items are equal

> Note that the return value on the inline function can be any > positive or negative number. Array.Sort() does not care what the > return number is. It only cares if the return value is positive, > negative or zero.

For Immutable sort: (example in ES6)

const sort = require('immutable-sort');
const array = [1, 5, 2, 4, 3];
const sortedArray = sort(array);

You can also write it this way:

import sort from 'immutable-sort';
const array = [1, 5, 2, 4, 3];
const sortedArray = sort(array);

The import-from you see is a new way to include javascript in ES6 and makes your code look very clean. My personal favorite.

Immutable sort doesn't mutate the source array rather it returns a new array. Using const is recommended on immutable data.

Solution 4 - Javascript

Here's a slightly modified version of @David Brainer-Bankers answer that sorts alphabetically by string, or numerically by number, and ensures that words beginning with Capital letters don't sort above words starting with a lower case letter (e.g "apple,Early" would be displayed in that order).

function sortByKey(array, key) {
    return array.sort(function(a, b) {
        var x = a[key];
        var y = b[key];
        
        if (typeof x == "string")
        {
	        x = (""+x).toLowerCase(); 
        }
        if (typeof y == "string")
        {
	        y = (""+y).toLowerCase();
        }

        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
}

Solution 5 - Javascript

Use underscore js or lodash,

var arrObj = [
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }
];

arrObj = _.sortBy(arrObj,"updated_at");

_.sortBy() returns a new array

refer http://underscorejs.org/#sortBy and lodash docs https://lodash.com/docs#sortBy

Solution 6 - Javascript

With ES2015 support it can be done by:

foo.sort((a, b) => a.updated_at < b.updated_at ? -1 : 1)

Solution 7 - Javascript

Data Imported

[    {        "gameStatus": "1",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-20 11:32:04"    },    {        "gameStatus": "0",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-19 18:08:24"    },    {        "gameStatus": "2",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-19 18:35:40"    },    {        "gameStatus": "0",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-19 10:42:53"    },    {        "gameStatus": "2",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-20 10:54:09"    },    {        "gameStatus": "0",        "userId": "1a2fefb0-5ae2-47eb-82ff-d1b2cc27875a",        "created_at": "2018-12-19 18:46:22"    },    {        "gameStatus": "1",        "userId": "7118ed61-d8d9-4098-a81b-484158806d21",        "created_at": "2018-12-20 10:50:48"    }]

FOR Ascending order

arr.sort(function(a, b){
    var keyA = new Date(a.updated_at),
        keyB = new Date(b.updated_at);
    // Compare the 2 dates
    if(keyA < keyB) return -1;
    if(keyA > keyB) return 1;
    return 0;
});

Example for Asc Order

[    {        "gameStatus": "0",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-19 10:42:53"    },    {        "gameStatus": "0",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-19 18:08:24"    },    {        "gameStatus": "2",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-19 18:35:40"    },    {        "gameStatus": "0",        "userId": "1a2fefb0-5ae2-47eb-82ff-d1b2cc27875a",        "created_at": "2018-12-19 18:46:22"    },    {        "gameStatus": "1",        "userId": "7118ed61-d8d9-4098-a81b-484158806d21",        "created_at": "2018-12-20 10:50:48"    },    {        "gameStatus": "2",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-20 10:54:09"    },    {        "gameStatus": "1",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-20 11:32:04"    }]

FOR Descending order

arr.sort(function(a, b){
    var keyA = new Date(a.updated_at),
        keyB = new Date(b.updated_at);
    // Compare the 2 dates
    if(keyA > keyB) return -1;
    if(keyA < keyB) return 1;
    return 0;
});

Example for Desc Order

[    {        "gameStatus": "1",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-20 11:32:04"    },    {        "gameStatus": "2",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-20 10:54:09"    },    {        "gameStatus": "1",        "userId": "7118ed61-d8d9-4098-a81b-484158806d21",        "created_at": "2018-12-20 10:50:48"    },    {        "gameStatus": "0",        "userId": "1a2fefb0-5ae2-47eb-82ff-d1b2cc27875a",        "created_at": "2018-12-19 18:46:22"    },    {        "gameStatus": "2",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-19 18:35:40"    },    {        "gameStatus": "0",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-19 18:08:24"    },    {        "gameStatus": "0",        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",        "created_at": "2018-12-19 10:42:53"    }]

Solution 8 - Javascript

As This answer's states, you can use Array.sort.

arr.sort(function(a,b){return new Date(a.updated_at) - new Date(b.updated_at)})

arr = [
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }
];
arr.sort(function(a,b){return new Date(a.updated_at) - new Date(b.updated_at)});
console.log(arr);

Solution 9 - Javascript

As for today, answers of @knowbody (https://stackoverflow.com/a/42418963/6778546) and @Rocket Hazmat (https://stackoverflow.com/a/8837511/6778546) can be combined to provide for ES2015 support and correct date handling:

arr.sort((a, b) => {
   const dateA = new Date(a.updated_at);
   const dateB = new Date(b.updated_at);
   return dateA - dateB;
});

Solution 10 - Javascript

You could use the Lodash utility library to solve this problem (it's quite an efficient library):

const data = [{
    "updated_at": "2012-01-01T06:25:24Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-09T11:25:13Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-05T04:13:24Z",
    "foo": "bar"
  }
]

const ordered = _.orderBy(
  data,
  function(item) {
    return item.updated_at;
  }
);

console.log(ordered)

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

You can find documentation here: https://lodash.com/docs/4.17.15#orderBy

Solution 11 - Javascript

Just another, more mathematical, way of doing the same thing but shorter:

arr.sort(function(a, b){
    var diff = new Date(a.updated_at) - new Date(b.updated_at);
    return diff/(Math.abs(diff)||1);
});

or in the slick lambda arrow style:

arr.sort((a, b) => {
    var diff = new Date(a.updated_at) - new Date(b.updated_at);
    return diff/(Math.abs(diff)||1);
});

This method can be done with any numeric input

Solution 12 - Javascript

With this we can pass a key function to use for the sorting

Array.prototype.sortBy = function(key_func, reverse=false){
    return this.sort( (a, b) => {
        var keyA = key_func(a),
            keyB = key_func(b);
        if(keyA < keyB) return reverse? 1: -1;
        if(keyA > keyB) return reverse? -1: 1;
        return 0;
    }); 
}

Then for example if we have

var arr = [ {date: "01/12/00", balls: {red: "a8",  blue: 10}},
            {date: "12/13/05", balls: {red: "d6" , blue: 11}},
            {date: "03/02/04", balls: {red: "c4" , blue: 15}} ]

We can do

arr.sortBy(el => el.balls.red)
/* would result in
[ {date: "01/12/00", balls: {red: "a8", blue: 10}},
  {date: "03/02/04", balls: {red: "c4", blue: 15}},
  {date: "12/13/05", balls: {red: "d6", blue: 11}} ]
*/

or

arr.sortBy(el => new Date(el.date), true)   // second argument to reverse it
/* would result in
[ {date: "12/13/05", balls: {red: "d6", blue:11}},
  {date: "03/02/04", balls: {red: "c4", blue:15}},
  {date: "01/12/00", balls: {red: "a8", blue:10}} ]
*/

or

arr.sortBy(el => el.balls.blue + parseInt(el.balls.red[1]))
/* would result in
[ {date: "12/13/05", balls: {red: "d6", blue:11}},    // red + blue= 17
  {date: "01/12/00", balls: {red: "a8", blue:10}},    // red + blue= 18
  {date: "03/02/04", balls: {red: "c4", blue:15}} ]   // red + blue= 19
*/

Solution 13 - Javascript

I have created a sorting function in Typescript which we can use to search strings, dates and numbers in array of objects. It can also sort on multiple fields.

export type SortType = 'string' | 'number' | 'date';
export type SortingOrder = 'asc' | 'desc';

export interface SortOptions {
  sortByKey: string;
  sortType?: SortType;
  sortingOrder?: SortingOrder;
}


class CustomSorting {
	static sortArrayOfObjects(fields: SortOptions[] = [{sortByKey: 'value', sortType: 'string', sortingOrder: 'desc'}]) {
	    return (a, b) => fields
	      .map((field) => {
	        if (!a[field.sortByKey] || !b[field.sortByKey]) {
	          return 0;
	        }

	        const direction = field.sortingOrder === 'asc' ? 1 : -1;

	        let firstValue;
	        let secondValue;

	        if (field.sortType === 'string') {
	          firstValue = a[field.sortByKey].toUpperCase();
	          secondValue = b[field.sortByKey].toUpperCase();
	        } else if (field.sortType === 'number') {
	          firstValue = parseInt(a[field.sortByKey], 10);
	          secondValue = parseInt(b[field.sortByKey], 10);
	        } else if (field.sortType === 'date') {
	          firstValue = new Date(a[field.sortByKey]);
	          secondValue = new Date(b[field.sortByKey]);
	        }
	        return firstValue > secondValue ? direction : firstValue < secondValue ? -(direction) : 0;

	      })
	      .reduce((pos, neg) => pos ? pos : neg, 0);
	  }
	}
}

Usage:

const sortOptions = [{
      sortByKey: 'anyKey',
      sortType: 'string',
      sortingOrder: 'asc',
    }];

arrayOfObjects.sort(CustomSorting.sortArrayOfObjects(sortOptions));

Solution 14 - Javascript

I'm getting here a little late, but in 2021 the correct answer is to use Intl.Collator. updated_at is an ISO-8601 string and thus sortable as a string. Converting to a Date is a waste of time and so is performing if comparisons manually to return 0, 1, or -1.

const arr = [
  {
    "updated_at": "2012-01-01T06:25:24Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-09T11:25:13Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-05T04:13:24Z",
    "foo": "bar"
  }
];

const { compare } = Intl.Collator('en-US');
arr.sort((a, b) => compare(a.updated_at, b.updated_at));

Intl.Collator returns a function that can be used as the compareFunction for #Array.sort. Because we're sorting an object, we call compare with the values of the key that we want to sort by.

Note that if we were sorting an array of strings, we could simply do this:

arr.sort(compare);

Also note that as others have pointed out, sort will mutate the original array. If this is undesirable, you may want to clone it first. In 2021 you can do so as follows:

[...arr].sort((a, b) => compare(a.updated_at, b.updated_at));

Solution 15 - Javascript

Sorting by an ISO formatted date can be expensive, unless you limit the clients to the latest and best browsers, which can create the correct timestamp by Date-parsing the string.

If you are sure of your input, and you know it will always be yyyy-mm-ddThh:mm:ss and GMT (Z) you can extract the digits from each member and compare them like integers

array.sort(function(a,b){
    return a.updated_at.replace(/\D+/g,'')-b.updated_at.replace(/\D+/g,'');
});

If the date could be formatted differently, you may need to add something for iso challenged folks:

Date.fromISO: function(s){
	var day, tz,
	rx=/^(\d{4}\-\d\d\-\d\d([tT ][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/,
	p= rx.exec(s) || [];
	if(p[1]){
		day= p[1].split(/\D/).map(function(itm){
			return parseInt(itm, 10) || 0;
		});
		day[1]-= 1;
		day= new Date(Date.UTC.apply(Date, day));
		if(!day.getDate()) return NaN;
		if(p[5]){
			tz= (parseInt(p[5], 10)*60);
			if(p[6]) tz+= parseInt(p[6], 10);
			if(p[4]== '+') tz*= -1;
			if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
		}
		return day;
	}
	return NaN;
}
if(!Array.prototype.map){
	Array.prototype.map= function(fun, scope){
		var T= this, L= T.length, A= Array(L), i= 0;
		if(typeof fun== 'function'){
			while(i< L){
				if(i in T){
					A[i]= fun.call(scope, T[i], i, T);
				}
				++i;
			}
			return A;
		}
	}
}
}

Solution 16 - Javascript

For completeness here is a possible short generic implementation of sortBy:

function sortBy(list, keyFunc) {
  return list.sort((a,b) => keyFunc(a) - keyFunc(b));
}

sortBy([{"key": 2}, {"key": 1}], o => o["key"])

Note that this uses the arrays sort method that sorts in place. for a copy you can use arr.concat() or arr.slice(0) or similar method to create a copy.

Solution 17 - Javascript

  • Use Array.sort() to sort an array
  • Clone array using spread operator () to make the function pure
  • Sort by desired key (updated_at)
  • Convert date string to date object
  • Array.sort() works by subtracting two properties from current and next item if it is a number / object on which you can perform arrhythmic operations
const input = [
  {
    updated_at: '2012-01-01T06:25:24Z',
    foo: 'bar',
  },
  {
    updated_at: '2012-01-09T11:25:13Z',
    foo: 'bar',
  },
  {
    updated_at: '2012-01-05T04:13:24Z',
    foo: 'bar',
  }
];

const sortByUpdatedAt = (items) => [...items].sort((itemA, itemB) => new Date(itemA.updated_at) - new Date(itemB.updated_at));

const output = sortByUpdatedAt(input);

console.log(input);
/*
[ { updated_at: '2012-01-01T06:25:24Z', foo: 'bar' }, 
  { updated_at: '2012-01-09T11:25:13Z', foo: 'bar' }, 
  { updated_at: '2012-01-05T04:13:24Z', foo: 'bar' } ]
*/
console.log(output)
/*
[ { updated_at: '2012-01-01T06:25:24Z', foo: 'bar' }, 
  { updated_at: '2012-01-05T04:13:24Z', foo: 'bar' }, 
  { updated_at: '2012-01-09T11:25:13Z', foo: 'bar' } ]
*/

Solution 18 - Javascript

I face with same thing, so i handle this with a generic why and i build a function for this:

> //example: > //array: [{name: 'idan', workerType: '3'}, {name: 'stas', workerType: '5'}, {name: 'kirill', workerType: '2'}] > //keyField: 'workerType' > // keysArray: ['4', '3', '2', '5', '6']

sortByArrayOfKeys = (array, keyField, keysArray) => {
    array.sort((a, b) => {
        const aIndex = keysArray.indexOf(a[keyField])
        const bIndex = keysArray.indexOf(b[keyField])
        if (aIndex < bIndex) return -1;
        if (aIndex > bIndex) return 1;
        return 0;
    })
}

Solution 19 - Javascript

You can create a closure and pass it that way here is my example working

$.get('https://data.seattle.gov/resource/3k2p-39jp.json?$limit=10&$where=within_circle(incident_location, 47.594972, -122.331518, 1609.34)', 
  function(responce) {

    var filter = 'event_clearance_group', //sort by key group name
    data = responce; 

    var compare = function (filter) {
        return function (a,b) {
            var a = a[filter],
                b = b[filter];
        
            if (a < b) {
                return -1;
            } else if (a > b) {
                return 1;
            } else {
                return 0;
            }
        };
    };

    filter = compare(filter); //set filter

    console.log(data.sort(filter));
});

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
Questionuser1022241View Question on Stackoverflow
Solution 1 - Javascriptgen_EricView Answer on Stackoverflow
Solution 2 - JavascriptDavid BrainerView Answer on Stackoverflow
Solution 3 - JavascriptPatrick W. McMahonView Answer on Stackoverflow
Solution 4 - JavascriptBrad ParksView Answer on Stackoverflow
Solution 5 - JavascriptMohammed SafeerView Answer on Stackoverflow
Solution 6 - JavascriptknowbodyView Answer on Stackoverflow
Solution 7 - JavascriptAnandan KView Answer on Stackoverflow
Solution 8 - JavascriptIshpreetView Answer on Stackoverflow
Solution 9 - JavascriptFyodorView Answer on Stackoverflow
Solution 10 - JavascriptChetanView Answer on Stackoverflow
Solution 11 - JavascriptDanny MorView Answer on Stackoverflow
Solution 12 - JavascriptaljgomView Answer on Stackoverflow
Solution 13 - JavascriptAnsumanView Answer on Stackoverflow
Solution 14 - JavascriptdonavonView Answer on Stackoverflow
Solution 15 - JavascriptkennebecView Answer on Stackoverflow
Solution 16 - JavascriptLegionView Answer on Stackoverflow
Solution 17 - JavascriptmarcobiedermannView Answer on Stackoverflow
Solution 18 - JavascriptIdanView Answer on Stackoverflow
Solution 19 - JavascriptMichael GuildView Answer on Stackoverflow