Find minimum time duration - javascript

I have an array of time duration strings as below, and would like to find the minimum time.
group = ["41:04", "54:50", "01:03:50"] // note this is mix of **mm:ss** and **HH:mm:ss**
I am using moment:
group.map(tid => moment.duration(tid,'hh:mm:ss').asSeconds());
but it interprets the first two elements as "hh:mm" instead of "mm:ss", and results in:
[147840, 197400, 3830]
However, the first element "41:04" is the shortest time duration.
Is there any way to get this right using moment? Or what is the best way to find the minimum time duration?
Note that if i concatenate zeros to the string by myself (ie, 00:41:04), it will be correct.

You could calculate the seconds elapsed using simple mathematics without using any libraries.
For example;
41:04 = 41 * 60 + 04
01:03:50 = 01 * (60 ^ 2) + 03 * 60 + 50
Creating simple function to calculate seconds elapsed.
const getSeconds = str => {
const sp = str.split(":");
let sum = 0;
sp.map((d, k) => {
sum += Number(d) * 60 ** (sp.length - 1 - k);
});
return sum;
};
Now you could loop through your array to get the seconds.
const min = group.reduce((d, k) => {
const a = getSeconds(d),
b = getSeconds(k);
return a < b ? d : k;
});
As a whole you could check out the code snippet below;
const group = ["50:04","41:04", "54:50", "01:03:50"];
const getSeconds = str => {
const sp = str.split(":");
let sum = 0;
sp.map((d, k) => {
sum += Number(d) * 60 ** (sp.length - 1 - k);
});
return sum;
};
const min = group.reduce((d, k) => {
const a = getSeconds(d),
b = getSeconds(k);
return a < b ? d : k;
});
console.log(min);
There might be more elegant solutions. But this is what I came up with. :D

Related

How can I convert a timestamp to seconds in java script

I want to convert this:
00:07:57,685
to seconds.
It should return 00*60 + 07*60 + 57,685
The problem is its format I did not manage to write an optimized function.
const input = "00:07:57,685";
const [hours, minutes, secondsRaw] = input.split(/:/g);
const seconds = secondsRaw.replace(",", ".");
let output = 0;
output += parseInt(hours) * 3600;
output += parseInt(minutes) * 60;
output += parseFloat(seconds);
console.log(`${output} seconds`);
Here is a working sample :
function stringTimeToSecond (stringTime) {
// convert from "," float notation to "." float notation
// split your string to [h, m, s]
// reverse to get [s, m, h] to be able to use indice on the reduce method
const stringTimeArray = stringTime.replace(',','.').split(":").reverse();
// 60^0 = 1 for seconds
// 60^1 = 60 for minutes
// 60^2 = 3600 for hours
return stringTimeArray.reduce((timeInSecond, time, i) => {
timeInSecond += time * Math.pow(60, i);
return timeInSecond;
}, 0);
}
Reduce method will iterate through your array and then return your accumulator "timeInSecond". The accumulator is initialized to 0 as the second argument of the reduce function.
I think this might work if i understood your question right:
let timestamp = "00:07:57,685"
let seconds = timestamp.split(":")[2].split(",")[0]

Best possible way for checking if an array of dates consist of n number of consecutive days or not in nodejs?

I have an array of stringified dates like,
arr = ["9-7-2020", "11-7-2020", "12-7-2020", "10-7-2020", "16-7-2020", "15-7-2020", "19-7-2020"]
now I want to check if this array consist of atleast 3 consecutive dates, so what should be the best possible way to do it in node?
One approach could be this, where we get and sort the timestamps of each date, and then we can check for consecutive dates by subtracting a day's milliseconds multiplied by its index and checking if the same timestamp occurs more than or equal to N times.
const arr = ["9-7-2020", "11-7-2020", "12-7-2020", "10-7-2020", "16-7-2020", "15-7-2020", "19-7-2020"];
const ONE_DAYS_MILLIS = 1000 * 60 * 60 * 24;
const hasNConsecutive = (dates, N) => {
// Remove duplicate dates if any
const uniqueDays = [... new Set(dates)];
const dateOccurrences = uniqueDays
.map((date) => date.split('-'))
// Get a timestamp for each date
.map(([day, month, year]) => new Date(year, month, day).getTime())
.sort()
// Since we have sorted the timestamps we can now check for
// consecutive dates by subtracting a day multiplied
// by the index and checking if the same timestamp
// occurs more than or equal to N times
.map((ts, index) => ts - index * ONE_DAYS_MILLIS)
.reduce((count, ts) => {
count[ts] = (count[ts] || 0) + 1;
return count;
},
{});
return Object.values(dateOccurrences).some((times) => times >= N);
};
const result = hasNConsecutive(arr, 3);
console.log(result);
Or if you are using lodash you can do the same thing a little bit easier.
const arr = ["9-7-2020", "11-7-2020", "12-7-2020", "10-7-2020", "16-7-2020", "15-7-2020", "19-7-2020"];
const ONE_DAYS_MILLIS = 1000 * 60 * 60 * 24;
const result = _
.chain(arr)
.uniq()
.map((date) => date.split('-'))
.map(([day, month, year]) => new Date(year, month, day).getTime())
.sort()
.map((ts, index) => ts - index * ONE_DAYS_MILLIS)
.countBy(_.identity)
.values()
.some((count) => count >= 3)
.value();
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
You could crate Date objects from those date string, sort them and then go through the array and check if the previous date plus one day is equal to the current date and keep a count of how many times that condition is true in a row.
Here is an example:
const arr = ["9-7-2020", "11-7-2020", "12-7-2020", "10-7-2020", "16-7-2020", "15-7-2020", "19-7-2020"]
const requiredConsecutiveCount = 3;
const getDate = (dateStr) => new Date(dateStr.split("-").reverse().map(v => parseInt(v)).map((v, i) => i === 1 ? (v + 1) : v));
const hasConsecutive = arr.map(getDate)
.sort((a, b) => a.getTime() - b.getTime())
.some(function(v, i, arr) {
if (i > 0) {
const tmp = new Date(arr[i - 1]);
tmp.setDate(tmp.getDate() + 1);
if (tmp.getTime() === v.getTime()) {
this.consecutiveCount++;
} else {
this.consecutiveCount = 0;
}
}
return this.consecutiveCount === requiredConsecutiveCount;
}, {
consecutiveCount: 0
});
console.log(hasConsecutive);
Probably my way of converting from a string date to a Date object is more complex than it has to be.
Two things needed here:
Sort the array by date, probably need to convert dates along the way
Then check if date diff to next element is 1 day and date diff to the element after that is 2 days
var arr = ["9-7-2020", "11-7-2020", "12-7-2020", "10-7-2020", "16-7-2020", "15-7-2020", "19-7-2020"]
var ONE_DAY = 24 * 60 * 60 * 1000;
function hasNConsecutive(arr, numConsecutive){
// new Date() is locale dependent. You might need to modify this heavily if you need to be locale-agnostic
var dateArray = arr.map(function(d){return new Date(d).getTime()}).sort();
var consecutiveCount = 1;
for(var i=1; i<dateArray.length; i++) {
if(dateArray[i] - dateArray[i-1] != ONE_DAY) {
consecutiveCount = 1;
continue;
}
consecutiveCount++;
if(consecutiveCount == numConsecutive) return true;
}
return false;
}
console.log(hasNConsecutive(arr, 3));
what's happening here is, we are making the month or the day a two digit if it isn't one, then we are constructing integer which has value like this "YYYYMMDD" then we are subtracting from three times of the first day with sum of three consecutive days, answer will be three if they are consecutive days
(caution: no duplicate dates must exist)
var arr = ["9-7-2020", "11-7-2020", "12-7-2020", "10-7-2020", "16-7-2020", "15-7-2020", "19-7-2020"]
arr.map((v) => (v.split("-").map((val) => (val.length == 1 ? "0" + val : val)).reverse().join(""))).map((value) => (parseInt(value))).sort((a, b) => (a > b ? 1 : a < b ? -1 : 0)).forEach((v, i, arr2) => {
if (i + 2 < arr2.length) {
var three = (v + arr2[i + 1] + arr2[i + 2]) - (v * 3)
if (three == 3) console.log('three consecutive days')
}
})

I need to extract every nth char of a string in Javascript

Ive been reading everything online but its not exactly what I need
var x = 'a1b2c3d4e5'
I need something to get me to
using 1 the answer should be abcde
using 2 the answer should be 12345
using 3 the answer should be b3e
the idea behind it if using 1 it grabs 1 skips 1
the idea behind it if using 2 it grabs 2 skips 2
the idea behind it if using 3 it grabs 3 skips 3
I dont want to use a for loop as it is way to long especially when your x is longer than 300000 chars.
is there a regex I can use or a function that Im not aware of?
update
I'm trying to some how implement your answers but when I use 1 that's when I face the problem. I did mention trying to stay away from for-loops the reason is resources on the server. The more clients connect the slower everything becomes. So far array.filter seem a lot quicker.
As soon as I've found it I'll accept the answer.
As others point out, it's not like regular expressions are magic; there would still be an underlying looping mechanism. Don't worry though, when it comes to loops, 300,000 is nothing -
console.time('while')
let x = 0
while (x++ < 300000)
x += 1
console.timeEnd('while')
// while: 5.135 ms
console.log(x)
// 300000
Make a big string, who cares? 300,000 is nothing -
// 10 chars repeated 30,000 times
const s =
'abcdefghij'.repeat(30000)
console.time('while string')
let x = 0
let interval = 2
let values = []
while (x < s.length)
{ values.push(s[x])
x += interval
}
let result = values.join('')
console.timeEnd('while string')
// while string: 31.990ms
console.log(result)
console.log(result.length)
// acegiacegiacegiacegiacegiacegiacegiacegia...
// 150000
Or use an interval of 3 -
const s =
'abcdefghij'.repeat(30000)
console.time('while string')
let x = 0
let interval = 3
let values = []
while (x < s.length)
{ values.push(s[x])
x += interval
}
let result = values.join('')
console.timeEnd('while string')
// while string: 25.055ms
console.log(result)
console.log(result.length)
// adgjcfibehadgjcfibehadgjcfibehadgjcfibe...
// 100000
Using a larger interval obviously results in fewer loops, so the total execution time is lower. The resulting string is shorter too.
const s =
'abcdefghij'.repeat(30000)
console.time('while string')
let x = 0
let interval = 25 // big interval
let values = []
while (x < s.length)
{ values.push(s[x])
x += interval
}
let result = values.join('')
console.timeEnd('while string')
// while string: 6.130
console.log(result)
console.log(result.length)
// afafafafafafafafafafafafafafafafafafafafafafa...
// 12000
You can achieve functional style and stack-safe speed simultaneously -
const { loop, recur } = require('./lib')
const everyNth = (s, n) =>
loop
( (acc = '', x = 0) =>
x >= s.length
? acc
: recur(acc + s[x], x + n)
)
const s = 'abcdefghij'.repeat(30000)
console.time('loop/recur')
const result = everyNth(s, 2)
console.timeEnd('loop/recur')
// loop/recur: 31.615 ms
console.log(result)
console.log(result.length)
// acegiacegiacegiacegiacegiacegiacegia ...
// 150000
The two are easily implemented -
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let acc = f()
while (acc && acc.recur === recur)
acc = f(...acc.values)
return acc
}
// ...
module.exports =
{ loop, recur, ... }
And unlike the [...str].filter(...) solutions which will always iterate through every element, our custom loop is much more flexible and receives speed benefit when a higher interval n is used -
console.time('loop/recur')
const result = everyNth(s, 25)
console.timeEnd('loop/recur')
// loop/recur: 5.770ms
console.log(result)
console.log(result.length)
// afafafafafafafafafafafafafafa...
// 12000
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let acc = f()
while (acc && acc.recur === recur)
acc = f(...acc.values)
return acc
}
const everyNth = (s, n) =>
loop
( (acc = '', x = 0) =>
x >= s.length
? acc
: recur(acc + s[x], x + n)
)
const s = 'abcdefghij'.repeat(30000)
console.time('loop/recur')
const result = everyNth(s, 2)
console.timeEnd('loop/recur')
// loop/recur: 31.615 ms
console.log(result)
console.log(result.length)
// acegiacegiacegiacegiacegiacegiacegia ...
// 150000
Since I'm not an expert of regex, I'd use some fancy es6 functions to filter your chars.
var x = 'a1b2c3d4e5'
var n = 2;
var result = [...x].filter((char, index) => index % n == 0);
console.log(result);
Note that because 0 % 2 will also return 0, this will always return the first char. You can filter the first char by adding another simple check.
var result = [...x].filter((char, index) => index > 0 && index % n == 0);
As a variant:
function getNth(str, nth) {
return [...str].filter((_, i) => (i + 1) % nth === 0).join('');
}
console.log(getNth('a1b2c3d4e5', 2)); // 12345
console.log(getNth('a1b2c3d4e5', 3)); // b3e
What I'd suggest, to avoid having to iterate over the entire array, is to step straight into the known nth's.
Here's a couple of flavors:
function nthCharSubstr(str, nth) {
let res = "";
for (let i = nth - 1; i < str.length; i += nth) {
res += string[i];
}
return res;
}
More ES6-y:
const nthCharSubstr = (str, nth) =>
[...Array(parseInt(str.length / nth)).keys()] // find out the resulting number of characters and create and array with the exact length
.map(i => nth + i * nth - 1) // each item in the array now represents the resulting character's index
.reduce((res, i) => res + str[i], ""); // pull out each exact character and group them in a final string
This solution considers this comment as being valid.

Generic formula for build an array of numbers of base-n

Say I want to build an array of numbers base 8, or base 26, I'm not sure how to approach a general formula for doing this:
console.log(arrayOfNumbersOfBase(8, 0, 10));
console.log(arrayOfNumbersOfBase(26, 0, 10));
function arrayOfNumbersOfBase(base, start, size)
{
var array = [];
for (var i = start, n = size; i < n; i++)
{
array.push(i * (base));
}
return array;
}
You can take the next approach as a starting point, basically I had to define some utility methods:
mapToChar(n) maps a number n to a character representation, for example, 10 is mapped to 'A'.
convertToBaseN(n, base) converts the number n to his representation on the given base. This method uses a recursive approach and utilizes the previous one.
Finally, generateNumbersOfBase(base, start, size) generates an array of size elements starting with the number start for the given base.
CODE:
// Next utility method map a decimal number to a character representation.
const mapToChar = (n) =>
{
n = (n >= 0 && n <= 9) ? '0'.charCodeAt() + n : n - 10 + 'A'.charCodeAt();
return String.fromCharCode(n);
}
// Next utility method convert a decimal number to his base-n representation.
const convertToBaseN = (n, base, res = "") =>
{
if (n <= 0)
return (res && res.split("").reverse().join("")) || "0";
// Convert input number to given base by repeatedly
// dividing it by base and taking remainder.
res += mapToChar(n % base);
return convertToBaseN(Math.floor(n / base), base, res);
}
// Next method generates an array of numbers for a given base.
const generateNumbersOfBase = (base, start, size) =>
{
return Array(size).fill(0).map((x, idx) => convertToBaseN(start + idx, base));
}
// Finally, generate some arrays.
let base10Array = generateNumbersOfBase(10, 15, 5);
let base2Array = generateNumbersOfBase(2, 5, 9);
let base16Array = generateNumbersOfBase(16, 10, 12);
let base8Array = generateNumbersOfBase(8, 1, 12);
console.log(
JSON.stringify(base10Array),
JSON.stringify(base2Array),
JSON.stringify(base16Array),
JSON.stringify(base8Array),
);
Now, if you need to convert some base-n representation back to decimal number, you can use next approach:
const convertToDec = (str, base) =>
{
let codeA = 'A'.charCodeAt();
let code0 = '0'.charCodeAt();
return str.split("").reverse().reduce((acc, c, idx) =>
{
let code = c.charCodeAt();
c = code + ((c >= '0' && c <= '9') ? -code0 : -codeA + 10);
return acc += c * Math.pow(base, idx);
}, 0);
}
// Lets convert back some arrays generated on the previous exampel
let base2Array = ["101","110","111","1000","1001","1010","1011","1100","1101"];
let base16Array = ["A","B","C","D","E","F","10","11","12","13","14","15"];
let res2 = base2Array.map(x => convertToDec(x, 2));
let res16 = base16Array.map(x => convertToDec(x, 16));
console.log(
JSON.stringify(res2),
JSON.stringify(res16)
);

Comparing current and next element of array and returning time difference

This is my array. Its length is about 9000. This is what a small bit of it looks like:
foreach_arr = ["21:07:01.535", "21:07:01.535", "21:07:26.113"]
There are a few occurences where the times diff is greater than a minute, and that is when I want to grab those times. And later use those times to get certain indices from another array. i.e "array"
I'm also using moment.js for time parsing.
Expected result: array = [8127, 9375, 13166, 14182]
Actual result: array = [8127, 13166]
Can't seem to find the issue here, I am getting 2 results when im supposed to be getting 4.
If the whole array is needed for troubleshooting, ill add it if I can.
var xx = foreach_arr.length - 1;
for(var z = 0; z < xx; z++) {
var current_row = foreach_arr[z];
var next_row = foreach_arr[z + 1];
var msElapsedTime = moment(next_row,"HH:mm:ss.SSS").diff(moment(current_row, "HH:mm:ss.SSS")) / 1000;
if(msElapsedTime > 60) {
attempt_indices.push(foreach_arr[z]);
}
}
for(var x = 0; x < attempt_indices.length; x++) {
array.push(newdata.indexOf(attempt_indices[x]));
}
Since the OP doesn't really need my code anymore, I'm posting it here to remove the downvote as much as anything else :)
const foreach_arr = ["21:07:01.535", "21:07:01.535", "21:07:26.113", '22:01:01.000'];
let processedForeach_arr = [];
let gtOneMinuteDiff = [];
foreach_arr.forEach((elem1, index1) => {
// elem1.split(':') turns foreach_arr[0] into ['21', '07', '01.535']
const splitElementArray = elem1.split(':');
let timeInMs = 0;
// this changes ['21', '07', '01.535'] into [75600000, 420000, 1535]
splitElementArray.forEach((elem2, index2) => {
if (index2 === 0) { // elem2 is hours. 3.6M ms per hour.
timeInMs += parseFloat(elem2) * 60 * 60 * 1000;
} else if (index2 === 1) { // elem2 is minutes. 60K ms per minute.
timeInMs += parseFloat(elem2) * 60 * 1000;
} else if (index2 === 2) { // elem2 is seconds. 1K ms per second.
timeInMs += parseFloat(elem2) * 1000;
} else {
throw `Expected array element formatted like HH:MM:SS.ms. Error on
element ${elem1}.`;
}
});
processedForeach_arr.push(timeInMs);
let timeDiff = processedForeach_arr[index1 - 1] - processedForeach_arr[index1];
if (Math.abs(timeDiff) > 60000) {
gtOneMinuteDiff.push(timeDiff);
}
});
To get the difference in milliseconds between foreach_arr[n] and foreach_arr[n+1], this code will
split each element of foreach_arr into 3 strings (hours, minutes, and seconds + milliseconds)
run parseFloat on each of those values to convert them to a number
convert all numbers to milliseconds and add them together
compare each consecutive value and return the difference.
Ok, I got this far and my son needs me. I'll finish out the code asap but you might beat me to it, hopefully the instructions above help.
turns out my code wasn't wrong. Just my idea of the whole proccess.
array = [8127, 13166]
is what I initialy get. With this, I use indexOf on my other array to eventually get my array as expected:
var another_test_arr = [];
for(var v = 0; v < array.length ; v++) {
var find = foreach_arr.indexOf(attempt_indices[v]);
another_test_arr.push(array[v], newdata.indexOf(foreach_arr[find + 1]));
}
Result: array = [8127, 9375, 13166, 14182]

Categories

Resources