0

For the following array-object-thing structure:

Events : {
    events : [
        {
          startDTG : {day: 0, month: 0, year: 0, time: "" },
          endDTG : {day: 0, month: 0, year: 0, time: "" },
          mode: ""
        },
        ...
    ],
    blah...,
    blah...,
    blah...
}

I am struggling to find a more efficient way to sort the events objects based on the startDTG key (Date-Time Group). Currently I use the following, but I feel there has to be a better way to do it!

SortEvents: function() {
    this.Events.events.sort(function(a, b){return a.startDTG.time - b.startDTG.time});
    this.Events.events.sort(function(a, b){return a.startDTG.day - b.startDTG.day});
    this.Events.events.sort(function(a, b){return a.startDTG.month - b.startDTG.month});
    this.Events.events.sort(function(a, b){return a.startDTG.year - b.startDTG.year});
},

Edit 1: The desire is to be sorted by Year > Month > Day > Time

I am at a critical point in which I am to abandon this custom DTG in the name of efficiency it is needed. I can post the entire code if requested, but might not make total sense as it is JS written to work within a Proprietary Control system called "Medialon"

Edit 2: Added a quick-made JSON code dump below to assist with readability of structure. Ignore the fact they are all "strings" it is how Medialon stringifies for persistence

{
  "events": [
    {
      "startDTG": {
        "day": "8",
        "month": "2",
        "year": "2019",
        "time": "06:35",
        "dayName": "5"
      },
      "endDTG": {
        "day": "9",
        "month": "2",
        "year": "2019",
        "time": "08:35",
        "dayName": "6"
      },
      "mode": "1"
    },
    {
      "startDTG": {
        "day": "27",
        "month": "2",
        "year": "2019",
        "time": "17:35",
        "dayName": "3"
      },
      "endDTG": {
        "day": "28",
        "month": "2",
        "year": "2019",
        "time": "06:35",
        "dayName": "4"
      },
      "mode": "1"
    },
    {
      "startDTG": {
        "day": "1",
        "month": "2",
        "year": "2019",
        "time": "14:35",
        "dayName": "5"
      },
      "endDTG": {
        "day": "2",
        "month": "2",
        "year": "2019",
        "time": "12:35",
        "dayName": "6"
      },
      "mode": "1"
    }
  ],
Shidersz
  • 16,846
  • 2
  • 23
  • 48
Antony T.
  • 15
  • 7
  • 1
    In the above, all your going to end up with is the events sorted by `year`,. So a more efficient way, is to not do the other sorts.. :) I'm assuming your wanting to sort by `year` then `month`, then `day` then `time`, but that's not how you do a compound sort. – Keith Feb 04 '19 at 21:12
  • Can you clean up the code sample a bit? It's hard to tell what's an array and what's an object. I think you are missing a few `{}`... – Garrett Motzner Feb 04 '19 at 21:12
  • You can separate the sorting criteria with `||` i.e. `this.Events.events.sort(function(a, b){return a.startDTG.year- b.startDTG.year || a.startDTG.month- b.startDTG.month || ... //etc})` – JohanP Feb 04 '19 at 21:13
  • 1
    @Keith: because pretty much all sort implementations are now stable, this will do what's desired: sort by year first, then month, then day. (Time as a string is more problematic). This is definitely not the most efficient, though. – Scott Sauyet Feb 04 '19 at 21:13
  • Are you trying to sort on StartDTG or EndDTG? Your inner events object is not well-formated, an array can't have keys – jo_va Feb 04 '19 at 21:19
  • What’s `time`? Your example shows an empty string so it’s hard to say, but if the real ones are strings like `"12:34"`, subtraction isn’t going to work very well. – Ry- Feb 04 '19 at 21:19
  • Time string is in 24 hr notation of HH:MM so thoughts are that 01:30 should always sort lower than 18:35 ect (still testing) – Antony T. Feb 04 '19 at 21:20
  • 1
    @ScottSauyet Edge doesn't have stable sort if that's an issue, and neither did Chrome until recently. And ES7 doesn't state a stable sort is a requirement, so I'd be very careful relying on such behaviour. – Keith Feb 04 '19 at 21:28
  • This might be helpful: https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value/4760279#4760279 – Garrett Motzner Feb 04 '19 at 21:31
  • Why are you storing your times this way in the first place, instead of just using a single property with a `Date` object? – Barmar Feb 04 '19 at 21:40
  • @Barmar At first I went down that route, but the Medialon program itself uses it's own odd date system which whenever you interface it is JS it comes in as 2 strings. One being a DD/MM/YYYY string the other being a HH:MM:SS/ms string. Being a scheduling system, I felt it less intensive to match their formatting to concat, compare and check for events rather than doing a lot of conversions.. Though this problem comes as a result. – Antony T. Feb 05 '19 at 14:02

2 Answers2

1

I'm still not quite sure of your data structure, but something like this should be close:

const events = [
  {name: 'a', startDTG: {year: 2019, month: 1,  day: 4,  time: '14:21:46'}, endDTG: ''},
  {name: 'b', startDTG: {year: 2018, month: 10, day: 7,  time: '12:13:59'}, endDTG: ''},
  {name: 'c', startDTG: {year: 2019, month: 1,  day: 4,  time: '09:23:51'}, endDTG: ''},
  {name: 'd', startDTG: {year: 2019, month: 1,  day: 2,  time: '15:02:36'}, endDTG: ''},
  {name: 'e', startDTG: {year: 2017, month: 9,  day: 17, time: '03:25:29'}, endDTG: ''},
  {name: 'f', startDTG: {year: 2017, month: 9,  day: 17, time: '03:25:28'}, endDTG: ''},
  {name: 'g', startDTG: {year: 2018, month: 4,  day: 14, time: '11:07:42'}, endDTG: ''},
]

events.sort((
  {startDTG: {year: y1, month: m1, day: d1, time: t1}}, 
  {startDTG: {year: y2, month: m2, day: d2, time: t2}}
) => 
  // y1 - y2 || m1 - m2 || d1 - d2 || (t1 < t2 ? -1 : t1 > t2 ? 1 : 0)           
  y1 - y2 || m1 - m2 || d1 - d2 || t1.localeCompare(t2)
)

console.log(events)
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
  • 2
    Yeah, that's much better. I wonder if `localeCompare` might be easier to read than the ternary for the times. – Mark Feb 04 '19 at 21:31
  • @MarkMeyer: yes, that definitely looks better. – Scott Sauyet Feb 04 '19 at 21:40
  • 1
    @MarkMeyer The OP mentioned efficient, so is maybe looking for the fastest. A tenery should be quicker, and a quick benchmark make localeCompare about 70% slower. – Keith Feb 04 '19 at 21:40
  • Well, they're both there for the OP to choose from. Either way, this should likely be quite a bit faster than performing four separate sorts. – Scott Sauyet Feb 05 '19 at 12:20
  • @ScottSauyet So there is a little bit of fenangling I have to do to get JS to work in this program. For the alias portion you did right after the .sort(), what is the technical name for it so I can read up on it a bit more. It doesn't work as a drop in snippit though it looks promising! – Antony T. Feb 05 '19 at 14:21
  • I have no idea what "a drop in snippit" means, but parameter desctucturing is described in [an MDN article](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Unpacking_fields_from_objects_passed_as_function_parameter). (The entire article is worth a read.) . It's pretty much equivalent to `...sort((a, b) => { const y1 = a.startDTG.year, m1 = a.startDTG.month, ... y2 = b.startDTG.year, ... t2 = b.startDTG.time; return y1 - y2 || ...}`. See also [Axel Rauschmayer's article](http://2ality.com/2015/01/es6-destructuring.html) for more info. – Scott Sauyet Feb 05 '19 at 16:09
1

Another solution could be mapping your data to a Date() and then comparing the milliseconds returned with getTime().

let data = {
  "events": [
    {
      "startDTG": {"day": "8", "month": "2", "year": "2019", "time": "6:35", "dayName": "5"},
      "endDTG": {"day": "9", "month": "2", "year": "2019", "time": "6:35", "dayName": "6"},
      "mode": "1"
    },
    {
      "startDTG": {"day": "27", "month": "2", "year": "2019", "time": "6:35", "dayName": "3"},
      "endDTG": {"day": "28", "month": "2", "year": "2019", "time": "6:35", "dayName": "4"},
      "mode": "1"
    },
    {
      "startDTG": {"day": "1", "month": "2", "year": "2019", "time": "6:35", "dayName": "5"},
      "endDTG": {"day": "2", "month": "2", "year": "2019", "time": "6:35", "dayName": "6"},
      "mode": "1"
    }
  ]
};

const startDTGToStr = o => `${o.year}-${o.month}-${o.day} ${o.time}`

data.events.sort((a, b) =>
{
    a = new Date(startDTGToStr(a.startDTG));
    b = new Date(startDTGToStr(b.startDTG));
    return a.getTime() - b.getTime();
});

console.log(data.events);
Shidersz
  • 16,846
  • 2
  • 23
  • 48
  • Although this is going to be faster than sorting an array 4 times, there is a bit of overhead in creating a Date object for every compare. And as such is slower than doing a compound compare like @Scott shows below. But if the OP had stored a DateTime in the first place, this would be the way to go. – Keith Feb 04 '19 at 21:53