如何将字符串类型的工作日数据项列表(日期、工作时间)缩减为工作日范围字符串列表,每个字符串都有总工作时间?
How does one reduce a list of string-type workday data-items (date, working hours) into a list of workday-range strings each with total working hours?
我有一个日期数组,我试图将它们连接起来以在 javascript 中创建一个日期范围。
[更新]:到目前为止,我一直在尝试将每个 month/day/year/hours 拆分成它们自己的数组,执行获取日期范围的功能,(尝试)拼接月份以便它们排成一行使用更新的串联日期范围,然后最后需要添加串联日期的所有时间。
所以起始数组:
["Nov 23 2021 8 hrs", "Nov 24 2021 8 hrs", "Nov 27 2021 8 hrs", "Dec 3 2021 8 hrs"]
应该是:
["Nov 23-24 2021 16 hrs", "Nov 27 2021 8 hrs", "Dec 3 2021 8 hrs"]
我能够成功拆分数组索引,将日期数字推入一个新数组,将它们连接起来,但我无法弄清楚如何根据第一个之后的任何日期摆脱额外的月份索引一.
这是日期连接的代码:
const getDayRanges = (array) => {
var ranges = [],
rstart,
rend;
for (var i = 0; i < array.length; i++) {
rstart = array[i];
rend = rstart;
while (array[i + 1] - array[i] == 1) {
rend = array[i + 1]; // increment the index if the numbers sequential
i++;
}
ranges.push(rstart == rend ? rstart + "" : rstart + " to " + rend);
}
return ranges;
};
我猜我需要传入整个数组并让它在嵌套的 for 循环中专门查找 array[i],或者为 [=28= 的函数添加一个新参数] 等来比较索引?不过,我对具体如何做到这一点有点困惑。
我认为将日期与时间保持一致的逻辑是有缺陷的。如果你把时间分开,比如在地图中,或者在二维数组中,那么实现你想要的会容易得多,使用 Date.parse 和新日期,你可以检查日期。像这样:
let date = new Date(Date.parse('Nov 23 2021'))
let date1 = new Date(Date.parse('Nov 24 2021'))
//and use Date methods to compare them
否则,您必须创建一个算法来匹配正则表达式,以便查找日期和时间、保留它们、比较它们、组合它们并将它们添加到新列表中。
这是一个经典的 map
/reduce
任务。
必须采取以下步骤...
map
将原始输入数据转换为方便的工作格式,并按日期(和小时)对每个项目进行排序。
映射后,reduce
通过尝试查找连续(或相同)天的范围来获取数据项。
在初始 map/reduce 任务(步骤 1+2)之后,再次 map
;这次将范围项数据简化为特殊的字符串化范围表示形式或原始输入格式。
另外...
不仅要处理一个月内的正常日期范围,还要处理跨越数月甚至数年的日期范围。
必须想出一个正则表达式来提取原始字符串的 data
和 hours
值 ...
function createWorkDayItem(record) {
const regXWorkDayData
= /(?<date>[A-Za-z]+\s+\d{1,2}\s+\d{4})\s+(?<hours>\d+(?:\.\d+)?)\s+hrs/;
const {
date = null,
hours = null,
} = regXWorkDayData.exec(record)?.groups ?? {};
return {
record,
hours: (hours !== null) ? Number(hours) : hours,
date,
time: (date !== null) ? new Date(date).getTime() : date,
};
}
function compareWorkDayItems(a, b) {
return a.time - b.time || b.hours - a.hours;
}
function collectItemOfSameOrConsecutiveDay(listOfRanges, currentItem, idx, arr) {
if (idx >= 1) {
const recentRange = listOfRanges.at(-1);
const recentRangeItem = recentRange.at(-1);
const deltaT = Number(
((currentItem.time - recentRangeItem.time) / 1000 /60 / 60 / 24) // msec => day(s)
.toFixed(4)
);
if (deltaT <= 1/* day*/) {
recentRange.push(currentItem)
} else {
listOfRanges.push([currentItem]);
}
} else {
listOfRanges.push([currentItem]);
}
return listOfRanges;
}
function createWorkDayRangeFormat(rangeItem) {
let result;
if (rangeItem.length === 1) {
result = rangeItem[0].record;
} else {
const totalHours = rangeItem
.reduce((total, { hours }) => total + hours, 0);
const [monthStart, dayStart, yearStart]
= rangeItem[0].date.split(/\s+/);
const [monthEnd, dayEnd, yearEnd]
= rangeItem.at(-1).date.split(/\s+/);
if (yearStart === yearEnd) {
if (monthStart === monthEnd) {
result = [
monthStart,
[dayStart, dayEnd].join('-'),
yearStart,
].join(' ');
} else {
result = [[
[monthStart, dayStart].join(' '),
[monthEnd, dayEnd].join(' '),
].join(' - '), yearEnd].join(' ');
}
} else {
result = [
[monthStart, dayStart, yearStart].join(' '),
[monthEnd, dayEnd, yearEnd].join(' '),
].join(' - ')
}
result = [result, totalHours, 'hrs'].join(' ');
}
return result;
}
const workDayHourList = [
"Nov 23 2021 8 hrs",
"Nov 24 2021 4.5 hrs",
"Nov 24 2021 2.5 hrs",
"Nov 27 2021 8 hrs",
"Dec 3 2021 8 hrs",
"Dec 16 2021 2.25 hrs",
"Dec 18 2021 7.75 hrs",
"Dec 17 2021 6.5 hrs",
"Dec 1 2021 4 hrs",
"Nov 30 2021 6 hrs",
"Dec 31 2020 3.5 hrs",
"Dec 30 2020 4.25 hrs",
"Jan 02 2021 3.75 hrs",
"Jan 01 2021 2.25 hrs",
];
console.log(
'1st ... map the raw input data and sort it by date and hours ...',
workDayHourList
.map(createWorkDayItem)
.sort(compareWorkDayItems)
);
console.log(
'2nd ...after mapping, reduce the data by trying to find ranges of consecutive days ...',
workDayHourList
.map(createWorkDayItem)
.sort(compareWorkDayItems)
.reduce(collectItemOfSameOrConsecutiveDay, [])
);
console.log(
'3rd ... after initial map/reduce, map again, the reduced (range) date into either a special range or into the original input format ...',
workDayHourList
.map(createWorkDayItem)
.sort(compareWorkDayItems)
.reduce(collectItemOfSameOrConsecutiveDay, [])
.map(createWorkDayRangeFormat)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
我有一个日期数组,我试图将它们连接起来以在 javascript 中创建一个日期范围。
[更新]:到目前为止,我一直在尝试将每个 month/day/year/hours 拆分成它们自己的数组,执行获取日期范围的功能,(尝试)拼接月份以便它们排成一行使用更新的串联日期范围,然后最后需要添加串联日期的所有时间。
所以起始数组:
["Nov 23 2021 8 hrs", "Nov 24 2021 8 hrs", "Nov 27 2021 8 hrs", "Dec 3 2021 8 hrs"]
应该是:
["Nov 23-24 2021 16 hrs", "Nov 27 2021 8 hrs", "Dec 3 2021 8 hrs"]
我能够成功拆分数组索引,将日期数字推入一个新数组,将它们连接起来,但我无法弄清楚如何根据第一个之后的任何日期摆脱额外的月份索引一.
这是日期连接的代码:
const getDayRanges = (array) => {
var ranges = [],
rstart,
rend;
for (var i = 0; i < array.length; i++) {
rstart = array[i];
rend = rstart;
while (array[i + 1] - array[i] == 1) {
rend = array[i + 1]; // increment the index if the numbers sequential
i++;
}
ranges.push(rstart == rend ? rstart + "" : rstart + " to " + rend);
}
return ranges;
};
我猜我需要传入整个数组并让它在嵌套的 for 循环中专门查找 array[i],或者为 [=28= 的函数添加一个新参数] 等来比较索引?不过,我对具体如何做到这一点有点困惑。
我认为将日期与时间保持一致的逻辑是有缺陷的。如果你把时间分开,比如在地图中,或者在二维数组中,那么实现你想要的会容易得多,使用 Date.parse 和新日期,你可以检查日期。像这样:
let date = new Date(Date.parse('Nov 23 2021'))
let date1 = new Date(Date.parse('Nov 24 2021'))
//and use Date methods to compare them
否则,您必须创建一个算法来匹配正则表达式,以便查找日期和时间、保留它们、比较它们、组合它们并将它们添加到新列表中。
这是一个经典的 map
/reduce
任务。
必须采取以下步骤...
map
将原始输入数据转换为方便的工作格式,并按日期(和小时)对每个项目进行排序。映射后,
reduce
通过尝试查找连续(或相同)天的范围来获取数据项。在初始 map/reduce 任务(步骤 1+2)之后,再次
map
;这次将范围项数据简化为特殊的字符串化范围表示形式或原始输入格式。
另外...
不仅要处理一个月内的正常日期范围,还要处理跨越数月甚至数年的日期范围。
必须想出一个正则表达式来提取原始字符串的
data
和hours
值 ...
function createWorkDayItem(record) {
const regXWorkDayData
= /(?<date>[A-Za-z]+\s+\d{1,2}\s+\d{4})\s+(?<hours>\d+(?:\.\d+)?)\s+hrs/;
const {
date = null,
hours = null,
} = regXWorkDayData.exec(record)?.groups ?? {};
return {
record,
hours: (hours !== null) ? Number(hours) : hours,
date,
time: (date !== null) ? new Date(date).getTime() : date,
};
}
function compareWorkDayItems(a, b) {
return a.time - b.time || b.hours - a.hours;
}
function collectItemOfSameOrConsecutiveDay(listOfRanges, currentItem, idx, arr) {
if (idx >= 1) {
const recentRange = listOfRanges.at(-1);
const recentRangeItem = recentRange.at(-1);
const deltaT = Number(
((currentItem.time - recentRangeItem.time) / 1000 /60 / 60 / 24) // msec => day(s)
.toFixed(4)
);
if (deltaT <= 1/* day*/) {
recentRange.push(currentItem)
} else {
listOfRanges.push([currentItem]);
}
} else {
listOfRanges.push([currentItem]);
}
return listOfRanges;
}
function createWorkDayRangeFormat(rangeItem) {
let result;
if (rangeItem.length === 1) {
result = rangeItem[0].record;
} else {
const totalHours = rangeItem
.reduce((total, { hours }) => total + hours, 0);
const [monthStart, dayStart, yearStart]
= rangeItem[0].date.split(/\s+/);
const [monthEnd, dayEnd, yearEnd]
= rangeItem.at(-1).date.split(/\s+/);
if (yearStart === yearEnd) {
if (monthStart === monthEnd) {
result = [
monthStart,
[dayStart, dayEnd].join('-'),
yearStart,
].join(' ');
} else {
result = [[
[monthStart, dayStart].join(' '),
[monthEnd, dayEnd].join(' '),
].join(' - '), yearEnd].join(' ');
}
} else {
result = [
[monthStart, dayStart, yearStart].join(' '),
[monthEnd, dayEnd, yearEnd].join(' '),
].join(' - ')
}
result = [result, totalHours, 'hrs'].join(' ');
}
return result;
}
const workDayHourList = [
"Nov 23 2021 8 hrs",
"Nov 24 2021 4.5 hrs",
"Nov 24 2021 2.5 hrs",
"Nov 27 2021 8 hrs",
"Dec 3 2021 8 hrs",
"Dec 16 2021 2.25 hrs",
"Dec 18 2021 7.75 hrs",
"Dec 17 2021 6.5 hrs",
"Dec 1 2021 4 hrs",
"Nov 30 2021 6 hrs",
"Dec 31 2020 3.5 hrs",
"Dec 30 2020 4.25 hrs",
"Jan 02 2021 3.75 hrs",
"Jan 01 2021 2.25 hrs",
];
console.log(
'1st ... map the raw input data and sort it by date and hours ...',
workDayHourList
.map(createWorkDayItem)
.sort(compareWorkDayItems)
);
console.log(
'2nd ...after mapping, reduce the data by trying to find ranges of consecutive days ...',
workDayHourList
.map(createWorkDayItem)
.sort(compareWorkDayItems)
.reduce(collectItemOfSameOrConsecutiveDay, [])
);
console.log(
'3rd ... after initial map/reduce, map again, the reduced (range) date into either a special range or into the original input format ...',
workDayHourList
.map(createWorkDayItem)
.sort(compareWorkDayItems)
.reduce(collectItemOfSameOrConsecutiveDay, [])
.map(createWorkDayRangeFormat)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }