#javascript
#javascript
Вопрос:
Я пытаюсь реализовать функцию, используя reduce
которая позволяет мне группировать массив объектов { date: '2019-03-11', count: 8 }
по неделям. До сих пор мне удавалось группировать даты по неделям, но у меня возникают проблемы с суммированием count
, если дата выпадает на ту же неделю.
const dates = [
{ date: '2019-03-11', count: 8 },
{ date: '2019-03-12', count: 7 },
{ date: '2019-03-09', count: 6 },
{ date: '2019-02-27', count: 10 },
{ date: '2019-02-26', count: 11 },
{ date: '2019-02-22', count: 12 },
{ date: '2019-04-21', count: 3 },
{ date: '2019-04-18', count: 2 },
{ date: '2019-04-17', count: 4 },
{ date: '2019-04-19', count: 5 }
];
Date.prototype.getWeek = function() {
const onejan = new Date(this.getFullYear(), 0, 1);
return Math.ceil(((this - onejan) / 86400000 onejan.getDay() 1) / 7);
};
const groups = dates.reduce(function(acc, item) {
const today = new Date(item.date);
const weekNumber = today.getWeek(today);
// check if the week number exists
if (typeof acc[weekNumber] === 'undefined') {
acc[weekNumber] = [];
}
acc[weekNumber].push(item.date, item.count);
return acc;
}, {});
console.log(groups);
Текущий результат
Желаемый результат
[
{ weekStart: '2019-02-17', count: 12 },
{ weekStart: '2019-02-24', count: 21 },
{ weekStart: '2019-03-03', count: 6 },
{ weekStart: '2019-03-10', count: 15 },
{ weekStart: '2019-04-14', count: 11 },
{ weekStart: '2019-04-21', count: 21 }
]
где weekStart
первая дата недели (воскресенье), по которой она была сгруппирована
РЕШЕНИЕ
const dates = [
{ date: '2019-02-24', count: 10 },
{ date: '2019-02-25', count: 11 },
{ date: '2019-02-26', count: 12 },
{ date: '2019-03-09', count: 8 },
{ date: '2019-03-10', count: 7 },
{ date: '2019-03-11', count: 6 },
{ date: '2019-04-14', count: 3 },
{ date: '2019-04-15', count: 2 },
{ date: '2019-04-16', count: 4 },
{ date: '2019-04-22', count: 5 }
];
/**
* Returns the week number for this date. dowOffset is the day of week the week
* "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday),
* the week returned is the ISO 8601 week number.
* @param int dowOffset
* @return int
*/
Date.prototype.getWeek = function(dowOffset) {
/*getWeek() was developed by Nick Baicoianu at MeanFreePath: http://www.epoch-calendar.com */
dowOffset = typeof dowOffset == 'int' ? dowOffset : 0; //default dowOffset to zero
var newYear = new Date(this.getFullYear(), 0, 1);
var day = newYear.getDay() - dowOffset; //the day of week the year begins on
day = day >= 0 ? day : day 7;
var daynum =
Math.floor(
(this.getTime() -
newYear.getTime() -
(this.getTimezoneOffset() - newYear.getTimezoneOffset()) * 60000) /
86400000
) 1;
var weeknum;
//if the year starts before the middle of a week
if (day < 4) {
weeknum = Math.floor((daynum day - 1) / 7) 1;
if (weeknum > 52) {
nYear = new Date(this.getFullYear() 1, 0, 1);
nday = nYear.getDay() - dowOffset;
nday = nday >= 0 ? nday : nday 7;
/*if the next year starts before the middle of
the week, it is week #1 of that year*/
weeknum = nday < 4 ? 1 : 53;
}
} else {
weeknum = Math.floor((daynum day - 1) / 7);
}
return weeknum;
};
function getWeekStart(date) {
var offset = new Date(date).getDay();
return new Date(new Date(date) - offset * 24 * 60 * 60 * 1000)
.toISOString()
.slice(0, 10);
}
function groupWeeks(dates) {
const groupsByWeekNumber = dates.reduce(function(acc, item) {
const today = new Date(item.date);
const weekNumber = today.getWeek();
// check if the week number exists
if (typeof acc[weekNumber] === 'undefined') {
acc[weekNumber] = [];
}
acc[weekNumber].push(item);
return acc;
}, []);
return groupsByWeekNumber.map(function(group) {
return {
weekStart: getWeekStart(group[0].date),
count: group.reduce(function(acc, item) {
return acc item.count;
}, 0)
};
});
}
console.log(groupWeeks(dates));
Комментарии:
1. У вас есть функция, которая создает первый день недели из номера недели?
2. @SalmanA вот функция, о которой вы просите
getWeekStart(date)
. У меня уже есть решение, но оно решается в два этапа. Я не думаю, что это лучшее решение. Если бы вы могли предложить лучшее решение, я был бы признателен. Существует также решение, предоставленное @Nina Scholz, но, по правде говоря, я его не совсем понимаю. К сожалению, слишком сложно для моего уровня
Ответ №1:
Вы могли бы взять день недели в качестве смещения и вычесть миллисекунды из заданной даты.
Сокращение работает с обратным вызовом и clsoure над ключом с датой начала недели.
(m, { date, count }) => // outer callback with map and object
(k => m.set(k, (m.get(k) || 0) count)) // closure over k and updating the count in map
(getWeekStart(date)) // get value for k
function getWeekStart(date) {
var offset = new Date(date).getDay();
return new Date(new Date(date) - offset * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
}
const
dates = [{ date: '2019-03-11', count: 8 }, { date: '2019-03-12', count: 7 }, { date: '2019-03-09', count: 6 }, { date: '2019-02-27', count: 10 }, { date: '2019-02-26', count: 11 }, { date: '2019-02-22', count: 12 }, { date: '2019-04-21', count: 3 }, { date: '2019-04-18', count: 2 }, { date: '2019-04-17', count: 4 }, { date: '2019-04-19', count: 5 }],
result = Array.from(
dates.reduce((m, { date, count }) =>
(k => m.set(k, (m.get(k) || 0) count))(getWeekStart(date)),
new Map),
([date, count]) => ({ date, count })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Комментарии:
1. это работает, но я не совсем понимаю, как это работает; (
2. Я приму ответ, но, по правде говоря, я впервые вижу такой синтез. Я действительно этого не понимаю.