6

Well, I have an array objects with random values, Ex.

var arr = [
    { id:1001, date:"20-02-2014", Name: 'demo1' },
    { id:1004, date:"13-02-2014", Name: 'demo0' },
    { id:1000, date:"10-02-2014", Name: 'demo14' },
    { id:1004, date:"16-02-2014", Name: 'demo10' },
    { id:1006, date:"22-02-2014", Name: 'demo111' },
    { id:1003, date:"28-02-2014", Name: 'demo16' },
    { id:1000, date:"28-01-2014", Name: 'demo12' },
    { id:1004, date:"28-01-2014", Name: 'demo01' },
    { id:1000, date:"08-01-2014", Name: 'demo41' },
    { id:1006, date:"08-01-2014", Name: 'demo91' }
]

I wanted to sort this array firstly by key id & then by key date as,

Output:

sorted_arr = [
 {"id":1000,"date":"08-01-2014","Name":"demo41"}, //group1
 {"id":1000,"date":"28-01-2014","Name":"demo12"}, //group1
 {"id":1000,"date":"10-02-2014","Name":"demo14"}, //group1 
 {"id":1001,"date":"20-02-2014","Name":"demo1"},  //group2
 {"id":1003,"date":"28-02-2014","Name":"demo16"}, //group3
 {"id":1004,"date":"28-01-2014","Name":"demo01"}, //group4
 {"id":1004,"date":"13-02-2014","Name":"demo0"},  //group4
 {"id":1004,"date":"16-02-2014","Name":"demo10"}, //group4
 {"id":1006,"date":"08-01-2014","Name":"demo91"}  //group5
 {"id":1006,"date":"22-02-2014","Name":"demo111"} //group5
]

I tried few generic code to sort,

    // generic comparison function
    cmp = function(x, y){
      return x > y ? 1 : x < y ? -1 : 0; 
    };


    arr.sort(function(a, b){
       return cmp( 
          [cmp(a.id, b.id), cmp(a.date, b.date)], 
          [cmp(b.id, a.id), cmp(b.date, a.date)]
       );
    });

I referred few examples SO Example but not getting expected output. Please suggest me best way to get this.

Community
  • 1
  • 1
Niks Jain
  • 1,617
  • 5
  • 27
  • 53
  • 2
    So what sort functions have you tried? It should do something like compare *id* and if they're different, return 1, 0 or -1 according to some rule. If they're the same, return 1, 0 or -1 depending on the value of *date* and the required sort order. Note that you'll likely have to convert the date string to a date object, or re–order the parts and compare as strings. – RobG Feb 24 '14 at 06:41
  • @RobG: Please check updated info. & Could please explain me this through few sample of code. that could really help me out to understand. – Niks Jain Feb 24 '14 at 06:46
  • possible duplicate of [Sorting objects in an array by a field value in JavaScript](http://stackoverflow.com/questions/1129216/sorting-objects-in-an-array-by-a-field-value-in-javascript) – Xotic750 Feb 24 '14 at 06:51
  • @Xotic750: Somehow its the same thing with different expected values. Its not giving me expected output. – Niks Jain Feb 24 '14 at 06:54
  • 1
    Please read the MDN reference for [`Array.sort`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) Your `sort` is incorrect and you need to understand how to use it. But yes, they are all the same thing, including what you are attempting. – Xotic750 Feb 24 '14 at 06:56
  • Uhh..I got it now..Thanks alot guys.. :) – Niks Jain Feb 24 '14 at 06:58

5 Answers5

8

No need to create Date objects, just reorder the date string into a sortable string, example

This example assumes that your dates are in the same format DD-MM-YYYY and creates YYYYMMDD for the date sort.

Javascript

var arr = [
    { id:1001, date:"20-02-2014", Name: 'demo1' },
    { id:1004, date:"13-02-2014", Name: 'demo0' },
    { id:1000, date:"10-02-2014", Name: 'demo14' },
    { id:1004, date:"16-02-2014", Name: 'demo10' },
    { id:1006, date:"22-02-2014", Name: 'demo111' },
    { id:1003, date:"28-02-2014", Name: 'demo16' },
    { id:1000, date:"28-01-2014", Name: 'demo12' },
    { id:1004, date:"28-01-2014", Name: 'demo01' },
    { id:1000, date:"08-01-2014", Name: 'demo41' },
    { id:1006, date:"08-01-2014", Name: 'demo91' }
];

var sorted = arr.sort(function (a, b) {
    return a.id - b.id || a.date.split('-').reverse().join('') - b.date.split('-').reverse().join('');
});

sorted.forEach(function (element) {
    console.log(JSON.stringify(element));
});

Output

{"id":1000,"date":"08-01-2014","Name":"demo41"}
{"id":1000,"date":"28-01-2014","Name":"demo12"}
{"id":1000,"date":"10-02-2014","Name":"demo14"}
{"id":1001,"date":"20-02-2014","Name":"demo1"} 
{"id":1003,"date":"28-02-2014","Name":"demo16"} 
{"id":1004,"date":"28-01-2014","Name":"demo01"} 
{"id":1004,"date":"13-02-2014","Name":"demo0"}
{"id":1004,"date":"16-02-2014","Name":"demo10"}
{"id":1006,"date":"08-01-2014","Name":"demo91"} 
{"id":1006,"date":"22-02-2014","Name":"demo111"} 

On jsFiddle

If there is any concern over mixing date formats, as discussed with @xdazz, then you can improve on this by checking the padding yourself. The following creates the format 'YYYYYYMMDD' when sorting by the date. The extra year padding is not necessary in this example as I am taking the numeric difference of the values, but if you choose to compare the strings then it is important.

function pad(s, n) {
    var v = '',
        i;

    for(i = 0; i < n - s.length; i += 1) {
        v += '0';
    }

    return v + s;
}

var sorted = arr.sort(function (a, b) {
    var idDiff = a.id - b.id;

    if (idDiff) {
        return idDiff;
    }

    var ordA = a.date.split('-').reverse(),
        ordB = b.date.split('-').reverse();

    ordA[0] = pad(ordA[0], 6);
    ordA[1] = pad(ordA[1], 2);
    ordA[2] = pad(ordA[2], 2);
    ordA = ordA.join('');
    ordB[0] = pad(ordB[0], 6);
    ordB[1] = pad(ordB[1], 2);
    ordB[2] = pad(ordB[2], 2);
    ordB = ordB.join('');
    return ordA - ordB;
});

On jsFiddle

If you really want to use Date objects the I would suggest the following.

var sorted = arr.sort(function (a, b) {
    var idDiff = a.id - b.id;

    if (idDiff) {
        return idDiff;
    }

    var ordA = a.date.split('-').reverse(),
        ordB = b.date.split('-').reverse();

    ordA[1] -= 1;
    ordB[1] -= 1;

    return new Date(Date.UTC.apply(undefined, ordA)).valueOf() -  new Date(Date.UTC.apply(undefined, ordB)).valueOf();
});

sorted.forEach(function (element) {
    console.log(JSON.stringify(element));
});

On jsFiddle

Note: These examples do not handle dates with negative years, again you would need to make further modifications.

Xotic750
  • 22,914
  • 8
  • 57
  • 79
5

First compare with id, then compare with date if id equal. But because your date is in invalid date format, extra work has to be done for letting it be recognized by Date.

sorted_arr = arr.sort(function(a, b) {
   return a.id - b.id || new Date(a.date.split('-').reverse().join('-')) - new Date(b.date.split('-').reverse().join('-'));
});

Edit: If you are guaranteed to have zeros in front of the 1-digit months and dates, then you could even not to parse to date:

sorted_arr = arr.sort(function(a, b) {
   return a.id - b.id || a.date.split('-').reverse().join('') - b.date.split('-').reverse().join('');
});
xdazz
  • 158,678
  • 38
  • 247
  • 274
  • He should be able to sort the date strings without the need to create a `Date` object, as they can easily be made a sortable format. – Xotic750 Feb 24 '14 at 06:59
  • 1
    +1. Indeed if one would use sortable date-time format like ISO8601 (YYYY-MM-DD) code would be simple. – Alexei Levenkov Feb 24 '14 at 06:59
  • 1
    @Xotic750 - DD-MM-YYYY is not sortable as text. – Alexei Levenkov Feb 24 '14 at 07:00
  • Master you deserve it +1 – Deepak Ingole Feb 24 '14 at 07:18
  • This sort can be affected by the users local time. It would be better to specify the time and the date component to prevent this. – Xotic750 Feb 24 '14 at 07:24
  • @Xotic750 How could it be affected by users local time, could you elaborate it? – xdazz Feb 24 '14 at 07:32
  • It should have read "could possibly" rather than "can" I suppose, it shouldn't I know, I am just saying that as a safe guard it would be better to specify the time component to avoid any such possibility. That's one reason that I would also avoid the `Date` object creation when a simple (cheaper) string reorder would achieve the same. And there is, of course, well known issues with string parsing (browser to browser compatabilities). – Xotic750 Feb 24 '14 at 07:46
  • Only since ECMA5 do they define [15.9.1.15](http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15) the ISO8601 variant (and even then some got it wrong), before that and even now other formats are implementation dependant. – Xotic750 Feb 24 '14 at 07:55
  • @Xotic750 ISO8601 date format is supported by all browsers. And date could not simply compared as string, what about `2014-8-1` and `2014-09-01`. – xdazz Feb 24 '14 at 07:58
  • No, ISO8601 is not supported by all browsers, and even some modern browsers have implementation errors. There is an article here on SO if I can find it. The OP is using zero padded dates in the example, it seems pretty safe to assume that they are not mixing formats, and if they are mixing them, then your string parsing can be in trouble too. – Xotic750 Feb 24 '14 at 08:03
  • @Xotic750 But you did the same string operation, didn't you? – xdazz Feb 24 '14 at 08:13
  • Yes, but I just compare the ordered string without creating a new `Date` object from them. That's an extra step that you perform that is not necessary. – Xotic750 Feb 24 '14 at 08:15
  • 1
    @Xotic750 OK, updated the answer if there are no mixing of zero padded dates and non-zero padded dates. – xdazz Feb 24 '14 at 08:23
  • Well, these comments caused me to update my answer too, regarding possible mixing of padded and non-padded. :) – Xotic750 Feb 24 '14 at 08:34
1

It's better to have a date operate lib momentjs to help. You could check the code on jsBin

function compare(a, b){
   var idDiff = a.id - b.id;
   var adate = moment(a.date, "DD-MM-YYYY");
   var bdate = moment(b.date, "DD-MM-YYYY");
   var dateDiff = adate.diff(bdate);
   return idDiff || dateDiff;
}

var sortedArr = arr.sort(compare);
console.log(sortedArr);
Lifecube
  • 1,198
  • 8
  • 11
1

You can sort array by two properties with Alasql library:

var res = alasql('SELECT *, mid(date,7,4)+mid(date,4,2)+mid(date,1,2) AS ndate \
    FROM ? ORDER BY id, ndate',[arr]);

Try this example at jsFiddle.

Here "mid(date,7,4)+mid(date,4,2)+mid(date,1,2)" was used to convert date from '28-11-2014' to the sort key like '20141128'.

agershun
  • 4,077
  • 38
  • 41
0

Came up with this using underscore.js & it's chain and sortBy:

var sorted_array = _(arr).chain().sortBy(function(o) {
    return o.date.split('-').reverse().join(); 
}).sortBy(function(o) {
    return o.id;
}).value();

Sorting on the date first, and then id will give you the list sorted as expected.

jsfiddle

Durandal
  • 5,575
  • 5
  • 35
  • 49