From cceecc071d1d13c4a12eda9e745e3da783baa6cb Mon Sep 17 00:00:00 2001 From: tina Date: Tue, 31 Jul 2018 15:06:16 +0200 Subject: [PATCH 1/3] computes the stats about the period lengths, started tests --- lib/periode-length.js | 35 +++++++++++++++++++++++++++++++++++ test/periode-length.spec.js | 16 ++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 lib/periode-length.js create mode 100644 test/periode-length.spec.js diff --git a/lib/periode-length.js b/lib/periode-length.js new file mode 100644 index 0000000..2445685 --- /dev/null +++ b/lib/periode-length.js @@ -0,0 +1,35 @@ +export default function getPeriodLengthStats(cycleLengthArray) { + if (Array.isArray(cycleLengthArray) && (cycleLengthArray.length > 0) && cycleLengthArray.every(cycleLength => { + return (typeof cycleLength === 'number') && !(isNaN(cycleLength)) + })) { + const PeriodLengthStats = {} + const sortedCycleLegthArray = cycleLengthArray.sort((a, b) => { + return a - b + }) + PeriodLengthStats.minimum = sortedCycleLegthArray[0] + PeriodLengthStats.maximum = sortedCycleLegthArray[cycleLengthArray.length - 1] + PeriodLengthStats.mean = cycleLengthArray.reduce(getSum) / cycleLengthArray.length + // median + if (cycleLengthArray.length % 2 == 1) { + PeriodLengthStats.median = sortedCycleLegthArray[(cycleLengthArray.length + 1) / 2 - 1] + } + else { + const middle = cycleLengthArray.length / 2 + PeriodLengthStats.median = (sortedCycleLegthArray[middle - 1] + sortedCycleLegthArray[middle]) / 2 + } + + PeriodLengthStats.stdDeviation = 0 // default + if (cycleLengthArray.length > 1) { + const sumOfSquares = cycleLengthArray.map(cycleLength => { + return Math.pow(cycleLength - PeriodLengthStats.mean, 2) + }).reduce(getSum) + PeriodLengthStats.stdDeviation = Math.sqrt(sumOfSquares / (cycleLengthArray.length - 1 )) + } + return PeriodLengthStats + } + console.error('getPeriodLengthStats requiers an array of numbers with length > 0.') +} + +function getSum(total, num) { + return total + num +} \ No newline at end of file diff --git a/test/periode-length.spec.js b/test/periode-length.spec.js new file mode 100644 index 0000000..3051244 --- /dev/null +++ b/test/periode-length.spec.js @@ -0,0 +1,16 @@ +import chai from 'chai' +import periodInfo from '../lib/period-length' + +const expect = chai.expect + +describe('it calculates the median correctly', () => { + it('works for an odd-numbered array', () => { + const periodLengths = [1, 2, 5, 99, 100] + const result = periodInfo(periodLengths).median + expect(result).to.eql(5) + }) + + /* it('works for an even-numbered array', () => { + + }) */ +}) \ No newline at end of file From 9664984e72c6b7e2fb7aa04ca20347035721afad Mon Sep 17 00:00:00 2001 From: tina Date: Wed, 1 Aug 2018 15:19:59 +0200 Subject: [PATCH 2/3] rounds period stats, adds tests --- lib/period-length.js | 46 ++++++++++++++++++++++++++++++++ lib/periode-length.js | 35 ------------------------- test/periode-length.spec.js | 52 +++++++++++++++++++++++++++++++------ 3 files changed, 90 insertions(+), 43 deletions(-) create mode 100644 lib/period-length.js delete mode 100644 lib/periode-length.js diff --git a/lib/period-length.js b/lib/period-length.js new file mode 100644 index 0000000..f369571 --- /dev/null +++ b/lib/period-length.js @@ -0,0 +1,46 @@ +import assert from 'assert' + +export default function getPeriodLengthStats(cycleLengths) { + throwIfArgsAreNotInRequiredFormat(cycleLengths) + const periodLengthStats = {} + const sortedCycleLengths = cycleLengths.sort((a, b) => { + return a - b + }) + periodLengthStats.minimum = sortedCycleLengths[0] + periodLengthStats.maximum = sortedCycleLengths[cycleLengths.length - 1] + periodLengthStats.mean = Math.round( + cycleLengths.reduce(getSum) / cycleLengths.length * 100) / 100 + // median + if (cycleLengths.length % 2 == 1) { + periodLengthStats.median = sortedCycleLengths[(cycleLengths.length + 1) / + 2 - 1] + } + else { + const middle = cycleLengths.length / 2 + periodLengthStats.median = (sortedCycleLengths[middle - 1] + + sortedCycleLengths[middle]) / 2 + } + // corrected standard deviation (based on unbiased sample variance) + periodLengthStats.stdDeviation = null // for case t + if (cycleLengths.length > 1) { + const sumOfSquares = cycleLengths.map(cycleLength => { + return Math.pow(cycleLength - periodLengthStats.mean, 2) + }).reduce(getSum) + periodLengthStats.stdDeviation = Math.round( + Math.sqrt(sumOfSquares / (cycleLengths.length - 1 )) * 100) / 100 + } + return periodLengthStats +} + +function getSum(total, num) { + return total + num +} + +function throwIfArgsAreNotInRequiredFormat(cycleLengths) { + assert.ok(Array.isArray(cycleLengths), 'Function requires input to be an array.') + assert.ok(cycleLengths.length > 0, 'Input array should not be empty.') + cycleLengths.forEach(cycleLength => { + assert.equal(typeof cycleLength, 'number', 'Elements in the array should be of type number.') + assert.ok(!isNaN(cycleLength), 'Elements of Array should not be NaN.') + }) +} \ No newline at end of file diff --git a/lib/periode-length.js b/lib/periode-length.js deleted file mode 100644 index 2445685..0000000 --- a/lib/periode-length.js +++ /dev/null @@ -1,35 +0,0 @@ -export default function getPeriodLengthStats(cycleLengthArray) { - if (Array.isArray(cycleLengthArray) && (cycleLengthArray.length > 0) && cycleLengthArray.every(cycleLength => { - return (typeof cycleLength === 'number') && !(isNaN(cycleLength)) - })) { - const PeriodLengthStats = {} - const sortedCycleLegthArray = cycleLengthArray.sort((a, b) => { - return a - b - }) - PeriodLengthStats.minimum = sortedCycleLegthArray[0] - PeriodLengthStats.maximum = sortedCycleLegthArray[cycleLengthArray.length - 1] - PeriodLengthStats.mean = cycleLengthArray.reduce(getSum) / cycleLengthArray.length - // median - if (cycleLengthArray.length % 2 == 1) { - PeriodLengthStats.median = sortedCycleLegthArray[(cycleLengthArray.length + 1) / 2 - 1] - } - else { - const middle = cycleLengthArray.length / 2 - PeriodLengthStats.median = (sortedCycleLegthArray[middle - 1] + sortedCycleLegthArray[middle]) / 2 - } - - PeriodLengthStats.stdDeviation = 0 // default - if (cycleLengthArray.length > 1) { - const sumOfSquares = cycleLengthArray.map(cycleLength => { - return Math.pow(cycleLength - PeriodLengthStats.mean, 2) - }).reduce(getSum) - PeriodLengthStats.stdDeviation = Math.sqrt(sumOfSquares / (cycleLengthArray.length - 1 )) - } - return PeriodLengthStats - } - console.error('getPeriodLengthStats requiers an array of numbers with length > 0.') -} - -function getSum(total, num) { - return total + num -} \ No newline at end of file diff --git a/test/periode-length.spec.js b/test/periode-length.spec.js index 3051244..ad10bd6 100644 --- a/test/periode-length.spec.js +++ b/test/periode-length.spec.js @@ -1,16 +1,52 @@ import chai from 'chai' +import { AssertionError } from 'assert' + import periodInfo from '../lib/period-length' const expect = chai.expect -describe('it calculates the median correctly', () => { - it('works for an odd-numbered array', () => { - const periodLengths = [1, 2, 5, 99, 100] - const result = periodInfo(periodLengths).median - expect(result).to.eql(5) +describe('getPeriodLengthStats', () => { + it('works for a simple odd-numbered array', () => { + const periodLengths = [99, 5, 1, 2, 100] + const result = periodInfo(periodLengths) + const expectedResult = { + minimum: 1, + maximum: 100, + mean: 41.4, + median: 5, + stdDeviation: 53.06 + } + expect(result).to.eql(expectedResult) }) - /* it('works for an even-numbered array', () => { - - }) */ + it('works for a simple even-numbered array', () => { + const periodLengths = [4, 1, 15, 2, 20, 5] + const result = periodInfo(periodLengths) + const expectedResult = { + minimum: 1, + maximum: 20, + mean: 7.83, + median: 4.5, + stdDeviation: 7.78 + } + expect(result).to.eql(expectedResult) + }) + describe('when args are wrong', () => { + it('throws when arg object is an empty array', () => { + const periodLengths = [] + expect(() => periodInfo(periodLengths).to.throw(AssertionError)) + }) + it('throws when arg object is not in right format', () => { + const wrongObject = { hello: 'world' } + expect(() => periodInfo(wrongObject).to.throw(AssertionError)) + }) + it('throws when arg array contains a string', () => { + const wrongElement = [4, 1, 15, '2', 20, 5] + expect(() => periodInfo(wrongElement).to.throw(AssertionError)) + }) + it('throws when arg array contains a NaN', () => { + const wrongElement = [4, 1, 15, NaN, 20, 5] + expect(() => periodInfo(wrongElement).to.throw(AssertionError)) + }) + }) }) \ No newline at end of file From 00a7de0faf02c3794d325ffc4ee8c1a1808e2e69 Mon Sep 17 00:00:00 2001 From: tina Date: Wed, 1 Aug 2018 15:49:44 +0200 Subject: [PATCH 3/3] little improvements and test for array of one element --- lib/period-length.js | 21 ++++++++++++--------- test/periode-length.spec.js | 14 +++++++++++++- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/period-length.js b/lib/period-length.js index f369571..e62f076 100644 --- a/lib/period-length.js +++ b/lib/period-length.js @@ -9,25 +9,28 @@ export default function getPeriodLengthStats(cycleLengths) { periodLengthStats.minimum = sortedCycleLengths[0] periodLengthStats.maximum = sortedCycleLengths[cycleLengths.length - 1] periodLengthStats.mean = Math.round( - cycleLengths.reduce(getSum) / cycleLengths.length * 100) / 100 + cycleLengths.reduce(getSum) / cycleLengths.length * 100 + ) / 100 // median if (cycleLengths.length % 2 == 1) { - periodLengthStats.median = sortedCycleLengths[(cycleLengths.length + 1) / - 2 - 1] - } - else { + periodLengthStats.median = sortedCycleLengths[ + (cycleLengths.length + 1) / 2 - 1 + ] + } else { const middle = cycleLengths.length / 2 periodLengthStats.median = (sortedCycleLengths[middle - 1] + sortedCycleLengths[middle]) / 2 } // corrected standard deviation (based on unbiased sample variance) - periodLengthStats.stdDeviation = null // for case t if (cycleLengths.length > 1) { const sumOfSquares = cycleLengths.map(cycleLength => { return Math.pow(cycleLength - periodLengthStats.mean, 2) }).reduce(getSum) periodLengthStats.stdDeviation = Math.round( - Math.sqrt(sumOfSquares / (cycleLengths.length - 1 )) * 100) / 100 + Math.sqrt(sumOfSquares / (cycleLengths.length - 1 )) * 100 + ) / 100 + } else { + periodLengthStats.stdDeviation = null } return periodLengthStats } @@ -37,10 +40,10 @@ function getSum(total, num) { } function throwIfArgsAreNotInRequiredFormat(cycleLengths) { - assert.ok(Array.isArray(cycleLengths), 'Function requires input to be an array.') + assert.ok(Array.isArray(cycleLengths), 'Input should be an array.') assert.ok(cycleLengths.length > 0, 'Input array should not be empty.') cycleLengths.forEach(cycleLength => { assert.equal(typeof cycleLength, 'number', 'Elements in the array should be of type number.') - assert.ok(!isNaN(cycleLength), 'Elements of Array should not be NaN.') + assert.ok(!isNaN(cycleLength), 'Elements of array should not be NaN.') }) } \ No newline at end of file diff --git a/test/periode-length.spec.js b/test/periode-length.spec.js index ad10bd6..0b9b200 100644 --- a/test/periode-length.spec.js +++ b/test/periode-length.spec.js @@ -5,7 +5,7 @@ import periodInfo from '../lib/period-length' const expect = chai.expect -describe('getPeriodLengthStats', () => { +describe.only('getPeriodLengthStats', () => { it('works for a simple odd-numbered array', () => { const periodLengths = [99, 5, 1, 2, 100] const result = periodInfo(periodLengths) @@ -31,6 +31,18 @@ describe('getPeriodLengthStats', () => { } expect(result).to.eql(expectedResult) }) + it('works for an one-element array', () => { + const periodLengths = [42] + const result = periodInfo(periodLengths) + const expectedResult = { + minimum: 42, + maximum: 42, + mean: 42, + median: 42, + stdDeviation: null + } + expect(result).to.eql(expectedResult) + }) describe('when args are wrong', () => { it('throws when arg object is an empty array', () => { const periodLengths = []