0

I am creating an array of objects, which looks like

let obj = {
    date: String,
    time: String,
    text: String,
  }

The date format is "MM-DD-YYYY" and format of time "HH:MM:SS" I am getting this from an API and can't change the format, I need to sort the array in increasing order of date and time, that is for the same date, increasing order of time. I have tried using JS Date and Moment, but was not able to do it. Sorting only by date works using Date(a.date) - Date(b.date), but if I try to include the time as well, it does not, not even (Date(a.date) - Date(b.date)) || (a.time.localeCompare(b.time))

How can I do this in JavaScript (NodeJS)?

  • Does this answer your question? [How to sort an array of objects by multiple fields?](https://stackoverflow.com/questions/6913512/how-to-sort-an-array-of-objects-by-multiple-fields) – derpirscher Apr 27 '23 at 16:11

3 Answers3

0

With the following sorting logic, you can has the date and time together so that you do not need to re-parse each item every time it is compared to another.

const main = () => {
  const sortedData = data.sort((a, b) =>
      retrieveByDateAndTime(a) - retrieveByDateAndTime(b)
    || a.text.localeCompare(b.text));

  hashLookup.clear(); // Clear the map (optional)

  console.log(sortedData); // Display the sorted data
}

// Optimize sorting speed by hashing
const hashLookup = new Map();
const parseDateAndTime = (date, time) => {
  const [_month, _date, _year] = date.split('-').map(v => parseInt(v, 10));
  const [_hour, _min, _sec] = time.split(':').map(v => parseInt(v, 10));
  return new Date(_year, _month - 1, _date, _hour, _min, _sec);
};
const retrieveByDateAndTime = ({ date, time }) => {
  const hash = generateHashCode(date + 'T' + time);
  let storedDate = hashLookup.get(hash);
  if (!storedDate) {
    storedDate = parseDateAndTime(date, time);
    hashLookup.set(hash, storedDate);
  }
  return storedDate;
};

// Credit: https://stackoverflow.com/a/8831937/1762224
const generateHashCode = (str) => {
  let hash = 0;
  for (let i = 0, len = str.length; i < len; i++) {
    let chr = str.charCodeAt(i);
    hash = (hash << 5) - hash + chr;
    hash |= 0;
  }
  return hash;
}

// Generated with https://www.mockaroo.com
const data = [
  { "date": "06-29-2022", "time": "14:27:00", "text": "Mustang"     },
  { "date": "11-10-2022", "time": "20:42:00", "text": "Firebird"    },
  { "date": "06-29-2022", "time": "00:22:00", "text": "Mustang"     },
  { "date": "11-10-2022", "time": "20:00:00", "text": "E-Series"    },
  { "date": "06-29-2022", "time": "10:58:00", "text": "E-Series"    },
  { "date": "05-18-2022", "time": "02:59:00", "text": "IS"          },
  { "date": "01-14-2023", "time": "09:15:00", "text": "Truck"       },
  { "date": "01-13-2023", "time": "00:17:00", "text": "CLS-Class"   },
  { "date": "11-10-2022", "time": "18:10:00", "text": "Oasis"       },
  { "date": "09-14-2022", "time": "04:15:00", "text": "Monte Carlo" }
];

main(); // Now, call main
.as-console-wrapper { top: 0; max-height: 100% !important; }
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
0

For each object create a properly formatted date-time string from the ["date"] and [time] strings.

const formattedDate = obj.date.split("-").join("/");
// "01-01-1970" to "01/01/1970"
const dateTime = formattedDate + " " obj.time;
// "01/01/1970 00:00:00"

Then convert the new string into a Date object with Date() constructor and then .parse() that into a timestamp number.

obj.stamp = Date.parse(new Date("01/01/1970 00:00:00"));
// 28800000

Details are commented om example

// Note the value of each ["text"] key implies object's true position.
const dataLog = [
  { date: "04-20-2001", time: "20:20:20", text: "5" },
  { date: "12-31-1999", time: "23:59:59", text: "3" },
  { date: "08-16-1998", time: "00:01:36", text: "1" },
  { date: "08-16-1998", time: "01:40:19", text: "2" },
  { date: "04-27-2023", time: "08:53:00", text: "6" },
  { date: "04-20-2001", time: "10:00:01", text: "4" }
];
// Utility function that formats and displays data to the console.
const log = data => console.log(JSON.stringify(data));

/**
 * Returns a given array of Objects in ascending order of each object
 * by the combined values of ["date"] and ["time"] as a new number value
 * under the added key ["stamp"].
 * @param {array<Object>} data - An array of Objects (see Object Example). 
 *                        Object Example 
 *                        { date: "01-01-1970", 
 *                          time: "00:00:00",
 *                          text: "0" }
 * @param {boolean}       clean - true: array is returned without timestamps. 
 *                                false: array is returned with timestamps.
 * @default {boolean}     clean - true
 * @returns {array<Object>} - See description above.
 */
const sortLog = (data, clean = true) => {
  let stamped = data.map(obj => {
    // Format ["date"] values from MM-DD-YYYY to MM/DD/YYYY
    const d = obj.date.split("-").join("/");
    /* Add ["stamp"] property. Value is the newly formatted
     * ["date"] value and ["time"] value parsed into a timestamp
     * (a number of msec since 01/01/1970).
     */
    obj.stamp = Date.parse(new Date(d+" "+obj.time));
    return obj;
  });
  // Sort each object in ascending order by ["stamp"] value.
  stamped.sort((a, b) => a.stamp - b.stamp);
  // Return array of Objects with timestamps if -clean is passed as false.
  return clean ? stamped.map(obj => {
    delete obj.stamp;
    return obj;
  }) : stamped;
}

// Without timestamps
log(sortLog(dataLog));
// With timestamps
log(sortLog(dataLog, false));
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • If you're going to reformat the strings, far better to use a supported format, e.g. YYYY-MM-DDTHH:mm:ss. An added bonus is that format will sort correctly as strings without parsing to a Date object. – RobG Apr 28 '23 at 11:23
-1

You can convert the date and time strings to JavaScript Date objects and then use the sort() method:

arr.sort(function(a, b) {
  // Convert date strings to Date objects
  let dateA = new Date(a.date + " " + a.time);
  let dateB = new Date(b.date + " " + b.time);

  // Compare dates and times
  if (dateA < dateB) {
    return -1;
  } else if (dateA > dateB) {
    return 1;
  } else {
    return 0;
  }
});
Klone
  • 290
  • 1
  • 2
  • I don't think that'll work. Since the format of the date is `MM-DD-YYYY`. – Mr. Polywhirl Apr 27 '23 at 15:44
  • Hoping that the built–in parser will correctly pares random format timestamps is not a good idea. `new Date('04-28-2023 15:35:21')` produces an invalid date in Safari and Firefox at least. – RobG Apr 28 '23 at 11:26
  • Thank you for pointing that out, do you have any link or source on why Safari and Firefox produces an invalid date ? – Klone May 02 '23 at 07:59