From 1aeaac8504876b3d954bb8d31da7b124678b7ce6 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sun, 14 Oct 2018 13:08:56 +0200 Subject: [PATCH] Don't do duplicate work in getLastMensesStart --- lib/cycle.js | 107 +++++++++++++++++++-------------------------- test/cycle.spec.js | 6 ++- 2 files changed, 49 insertions(+), 64 deletions(-) diff --git a/lib/cycle.js b/lib/cycle.js index 7deb02e..5393eb9 100644 --- a/lib/cycle.js +++ b/lib/cycle.js @@ -26,70 +26,49 @@ export default function config(opts) { minCyclesForPrediction = opts.minCyclesForPrediction || 3 } - function getLastMensesStart(targetDateString) { - const targetDate = LocalDate.parse(targetDateString) - const withWrappedDates = bleedingDaysSortedByDate - .filter(day => !day.bleeding.exclude) - .map(day => { - day.wrappedDate = LocalDate.parse(day.date) - return day - }) + function findLatestMensesStart(bleedingDays) { + if (!bleedingDays.length) return null - // the index of the first bleeding day before the target day - const index = withWrappedDates.findIndex(day => { - return ( - day.wrappedDate.isEqual(targetDate) || - day.wrappedDate.isBefore(targetDate) - ) - }) - - if (index < 0) { - withWrappedDates.forEach(day => delete day.wrappedDate) - return null - } - - const prevBleedingDays = withWrappedDates.slice(index) - - const lastMensesStart = prevBleedingDays.find((day, i) => { - return noBleedingDayWithinThreshold(day, prevBleedingDays.slice(i + 1)) + // assumes bleeding days are ordered latest first, and + // excluded values already removed + const lastMensesStart = bleedingDays.find((day, i) => { + return noBleedingDayWithinThreshold(day, bleedingDays.slice(i + 1)) }) function noBleedingDayWithinThreshold(day, previousBleedingDays) { - const periodThreshold = day.wrappedDate.minusDays(maxBreakInBleeding + 1) - return !previousBleedingDays.some(({ wrappedDate }) => { - return ( - wrappedDate.equals(periodThreshold) || - wrappedDate.isAfter(periodThreshold) - ) - }) + const localDate = LocalDate.parse(day.date) + const threshold = localDate.minusDays(maxBreakInBleeding + 1).toString() + return !previousBleedingDays.some(({ date }) => date >= threshold) } - withWrappedDates.forEach(day => delete day.wrappedDate) return lastMensesStart } - function getFollowingMensesStart(targetDateString) { - const targetDate = LocalDate.parse(targetDateString) - const withWrappedDates = bleedingDaysSortedByDate + function getLastMensesStartForDay(targetDateString) { + // the index of the first bleeding day before the target day + const index = bleedingDaysSortedByDate.findIndex(day => { + return day.date <= targetDateString && !day.bleeding.exclude + }) + + if (index < 0) return null + + const prevBleedingDays = bleedingDaysSortedByDate.slice(index) + return findLatestMensesStart(prevBleedingDays) + } + + function getFollowingMensesStartForDay(targetDateString) { + const followingBleedingDays = bleedingDaysSortedByDate .filter(day => !day.bleeding.exclude) - .map(day => { - day.wrappedDate = LocalDate.parse(day.date) - return day - }) - - const firstBleedingDayAfterTargetDay = withWrappedDates .reverse() - .find(day => { - return day.wrappedDate.isAfter(targetDate) - }) - withWrappedDates.forEach(day => delete day.wrappedDate) + const firstBleedingDayAfterTargetDay = followingBleedingDays + .find(day => day.date > targetDateString) return firstBleedingDayAfterTargetDay } function getCycleDayNumber(targetDateString) { - const lastMensesStart = getLastMensesStart(targetDateString) + const lastMensesStart = getLastMensesStartForDay(targetDateString) if (!lastMensesStart) return null const targetDate = LocalDate.parse(targetDateString) const lastMensesLocalDate = LocalDate.parse(lastMensesStart.date) @@ -111,7 +90,7 @@ export default function config(opts) { } function getPreviousCycle(dateString) { - const startOfCycle = getLastMensesStart(dateString) + const startOfCycle = getLastMensesStartForDay(dateString) if (!startOfCycle) return null const dateBeforeStartOfCycle = LocalDate .parse(startOfCycle.date) @@ -123,10 +102,10 @@ export default function config(opts) { function getCycleForDay(dayOrDate) { const dateString = typeof dayOrDate === 'string' ? dayOrDate : dayOrDate.date - const cycleStart = getLastMensesStart(dateString) + const cycleStart = getLastMensesStartForDay(dateString) if (!cycleStart) return null const cycleStartIndex = cycleDaysSortedByDate.indexOf(cycleStart) - const nextMensesStart = getFollowingMensesStart(dateString) + const nextMensesStart = getFollowingMensesStartForDay(dateString) if (nextMensesStart) { return cycleDaysSortedByDate.slice( cycleDaysSortedByDate.indexOf(nextMensesStart) + 1, @@ -137,16 +116,20 @@ export default function config(opts) { } } - function getAllMensesStarts(day, collectedDates) { - day = day || LocalDate.now().toString() - collectedDates = collectedDates || [] - const lastStart = getLastMensesStart(day) - if (!lastStart) { - return collectedDates - } else { - const newDay = LocalDate.parse(lastStart.date).minusDays(1).toString() - collectedDates.push(lastStart.date) - return getAllMensesStarts(newDay, collectedDates) + function getAllMensesStarts(initialBleedingDays = bleedingDaysSortedByDate) { + return recurse(initialBleedingDays.filter(d => !d.bleeding.exclude)) + + function recurse(bleedingDays, collectedDates) { + collectedDates = collectedDates || [] + const lastStart = findLatestMensesStart(bleedingDays) + if (!lastStart) { + return collectedDates + } else { + collectedDates.push(lastStart.date) + const index = bleedingDays.indexOf(lastStart) + const remainingDays = bleedingDays.slice(index + 1) + return recurse(remainingDays, collectedDates) + } } } @@ -189,8 +172,8 @@ export default function config(opts) { lastStart = lastStart.plusDays(periodDistance) const nextPredictedDates = [lastStart.toString()] for (let j = 0; j < periodStartVariation; j++) { - nextPredictedDates.push(lastStart.minusDays(j+1).toString()) - nextPredictedDates.push(lastStart.plusDays(j+1).toString()) + nextPredictedDates.push(lastStart.minusDays(j + 1).toString()) + nextPredictedDates.push(lastStart.plusDays(j + 1).toString()) } nextPredictedDates.sort() predictedMenses.push(nextPredictedDates) diff --git a/test/cycle.spec.js b/test/cycle.spec.js index e3b5111..9d16139 100644 --- a/test/cycle.spec.js +++ b/test/cycle.spec.js @@ -625,7 +625,8 @@ describe('getAllMensesStart', () => { const result = getAllMensesStarts() expect(result.length).to.eql(2) expect(result).to.eql(['2018-06-01', '2018-05-01']) - }), + }) + it('works for two cycle starts with excluded data', () => { const cycleDaysSortedByDate = [ { @@ -649,7 +650,8 @@ describe('getAllMensesStart', () => { const result = getAllMensesStarts() expect(result.length).to.eql(2) expect(result).to.eql(['2018-06-01', '2018-05-01']) - }), + }) + it('returns an empty array if no bleeding days are given', () => { const cycleDaysSortedByDate = [ {} ]