0

I have an array of strings corresponding to Date objects. like this:

const timestamps =  [
  '2023-03-15T10:47:38.878Z',
  '2023-03-15T10:46:51.375Z',
  '2023-03-15T10:46:39.645Z',
  '2023-03-15T10:47:19.072Z',
  '2023-03-15T10:46:20.395Z'
]

var convertedTimestamps = [];
for (var time in timestamps) {
  var timeAsDate = Date.parse(time);
  console.log(time, timeAsDate);
  convertedTimestamps.push(timeAsDate);
}
const min = Math.min(...convertedTimestamps);

They are Date objects parsed into strings for other purposes This can be any length really, this is just one instance of it. I want to find the oldest of the timestamps.

In the example above, these are the converted timestamps, and the min value is correct based on the numbers, but not the date objects. The top one is the one that is removed

946681200000
978303600000
980982000000
983401200000
986076000000
min: 946681200000
'2023-03-15T10:47:38.878Z'

It might just be a math problem, but the way i understand it, a string parsed like this returns an int representing how much time has passed since 1970 etc, and so the oldest should be closest to that time should it not? this min value is in fact the most recent of the timestamps. When i try the max value it is not correct either.

derpirscher
  • 14,418
  • 3
  • 18
  • 35
gggaute
  • 1
  • 1
  • Does this answer your question? [Loop (for each) over an array in JavaScript](https://stackoverflow.com/questions/9329446/loop-for-each-over-an-array-in-javascript) – derpirscher Mar 15 '23 at 11:43
  • I converted your script into a runable example. Have a look what values `time` actually has and what is parsed to a date. Use a `for .. of` loop instead of a `for .. in`. Otherwise your code is fine ... – derpirscher Mar 15 '23 at 11:44

4 Answers4

0

Think you can solve this problem by adapting the loop in your case. The loop should look like this:

for (var i = 0; i < timestamps.length; i++) {
    var timeAsDate = Date.parse(timestamps[i]); # just adapted this to the loop
    ...
}

This should do the work for your case since the the loop before iterates over the keys of the array rather than the elements themselves which leads to a incorrect Date parsing because it tries to parse the index instead of the timestamp in that case.

41 72 6c
  • 1,600
  • 5
  • 19
  • 30
  • While that answer solves the problem, you should also state *why* using a `for .. in` loop is wrong here ... (or just close it as a duplicate) – derpirscher Mar 15 '23 at 11:45
  • @derpirscher thanks for that hint! just edited my answer. – 41 72 6c Mar 15 '23 at 11:48
  • But your explanation is wrong. A `for .. in` loop *does not* iterate over the *elements* of an array (if it was, OP's code would work), but over the *keys of an object*, ie the `time` in OP's code gets the values `"0"`, `"1"`,`"2"`,`"3"`,`"4"` which is obviously wrong – derpirscher Mar 15 '23 at 11:53
  • mistaken that. what is meant with indexes is, that the loop is iterating over an objects property, which in the case of an array would be the indexes (key) and not the elements. – 41 72 6c Mar 15 '23 at 12:00
0

Since you're dealing with ISO 8601 timestamps, you can compare any string against it

  1. Set lowest to a, an high value string
  2. Loop over timestamps
  3. If the current on is lower, overwrite
  4. Log

const timestamps = [
  '2023-03-15T10:47:38.878Z',
  '2023-03-15T10:46:51.375Z',
  '2023-03-15T10:46:39.645Z',
  '2023-03-15T10:47:19.072Z',
  '2023-03-15T10:46:20.395Z'
];
let lowest = 'a';

for (let timestamp of timestamps) {
    if (lowest > timestamp) {
        lowest = timestamp;
    }
}

console.log('Lowest timestamp: ' + lowest)
// Lowest timestamp: 2023-03-15T10:46:20.395Z

Thanks to @Derpirscher for commenting this.


Useful reads:

0stone0
  • 34,288
  • 4
  • 39
  • 64
  • 1
    Actually, with ISO-Timestamps there is no need to parse. you can just compare them by string comparison. And it would also be more efficient to initialize the `lower` with a value like `Number.MAX_SAFE_INTEGER`, you you won't need to check for `0` in every iteration. – derpirscher Mar 15 '23 at 11:22
  • 1
    Sorry if my comment was unclear, but with your usage of `MAX_SAFE_INTEGER.ToString()` it's just pure coincidence that this code works, because `MAX_SAFE_INTEGER` is `9007199254740991` ... What if it was `19007199254740991`? I meant if you compare by *numeric value* (ie with parsing) initialize it with `MAX_SAFE_INTEGER`. If you compare as string, just use `let lower = '9'` or to be completely sure (if you have to deal with year 9999) `let lower = 'a'` – derpirscher Mar 15 '23 at 11:49
0

If all dates are following the format mentioned, it's enough to string-sort that array and get the first entry, since the string is "sorted" from longest time-unit to shortest.

So to get the oldest entry:

const timestamps =  [
  '2023-03-15T10:47:38.878Z',
  '2023-03-15T10:46:51.375Z',
  '2023-03-15T10:46:39.645Z',
  '2023-03-15T10:47:19.072Z',
  '2023-03-15T10:46:20.395Z'
];
const lowest = timestamps.sort()[0];
boppy
  • 1,753
  • 12
  • 10
  • To readers: Note that `.sort`'s time complexity is `O(n log n)` while that of a simple loop is `O(n)`. – InSync Mar 15 '23 at 11:21
  • Sorting to find the maximum/minimum is turing an `O(n)` task into `O(n logn)`. May not be relevant for small datasets, but still not best practice ... – derpirscher Mar 15 '23 at 11:27
  • Question is, if that results in longer runtime, since I think `.sort` might be faster (better optimized) than a self-implemented sort as mentioned. And will surely be faster than parsing each entry on its own to a Date-object... – boppy Mar 15 '23 at 11:27
  • There is no "sorting" necessary. Just iterate over the collection once and remember the current smallest element. This surely is faster than any (even if builtin and optimized) sorting algorithm ... – derpirscher Mar 15 '23 at 12:00
0

for..in is used to iterate over the properties of an object, not an array. If you add console.log(time) in your loop you will see that time is a string representing each index of the array. If you change to for..of your code will work.

const timestamps = [
  '2023-03-15T10:47:38.878Z',
  '2023-03-15T10:46:51.375Z',
  '2023-03-15T10:46:39.645Z',
  '2023-03-15T10:47:19.072Z',
  '2023-03-15T10:46:20.395Z'
]

for (var time in timestamps) {
  console.log(time);
}

for (var time of timestamps) {
  console.log(time);
}

Also, unless you need the convert timestamps for something else, you might want to simply get the minimum in your loop.

const timestamps = [
  '2023-03-15T10:47:38.878Z',
  '2023-03-15T10:46:51.375Z',
  '2023-03-15T10:46:39.645Z',
  '2023-03-15T10:47:19.072Z',
  '2023-03-15T10:46:20.395Z'
];

let dateTime;
let minDateTime = Date.parse(timestamps[0]);
for (var time of timestamps) {
  dateTime = Date.parse(time);
  if (dateTime < minDateTime)
    minDateTime = dateTime;
}

console.log((new Date(minDateTime)).toISOString());
rjax
  • 39
  • 5
  • You have to be a bit careful with your initialization of `let minDateTime = Date.parse(timestamps[0]);` Your code will throw an error if the `timestamps` array is empty ... – derpirscher Mar 15 '23 at 14:39