Skip to content

Commit f7a9dea

Browse files
refactor[2020-day-01]: use recursion for expense records checksum search
This will allow for 2020 Day 01 Part 2 to specify an arbitrary number of expense reports
1 parent 12c6823 commit f7a9dea

File tree

1 file changed

+48
-11
lines changed

1 file changed

+48
-11
lines changed

2020/day-01/expenseValidation.js

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,62 @@
22
/**
33
* Validates a list of records by comparing every combination
44
* to the checksum. Stops when the first match is found
5+
* @param {array} records List of records to check
6+
* @param {int} checksum The target sum that records should add up to
7+
* @param {int} goal The number of records we hope to find
58
*/
6-
const validateRecords = (records, checksum = 2020) => {
9+
const validateRecords = (records, checksum = 2020, goal = 2) => {
710
const results = []
811

9-
// We're using Array.find() at each level so it stops looping
10-
// onced matched. This game has a habit of throwing huge
11-
// data sets to discourage brute-forcing
12-
const matcher = records.find((record) => {
13-
const match = records.find(matchRec => record + matchRec === checksum)
14-
if (match) {
15-
results.push(match)
12+
const obj = { foo: 'bar' }
13+
// Intentionally using `function()` instead of `() =>` because
14+
// the thisArg won't get passed to the find callback otherwise
15+
// https://stackoverflow.com/questions/46639131/javascript-array-prototype-find-second-argument-thisarg-not-working
16+
function testRecursive (record, idx, arr, parent) {
17+
console.log(`recurse check ${record} - `, parent)
18+
console.log(this)
19+
}
20+
records.find(testRecursive, obj)
21+
22+
function matcher (record) {
23+
this.depth = this.depth || 1 // depth tracking starts at level 1
24+
this.tracker = this.tracker || 0 // for basic sums, start counter at 0
25+
const subTotal = this.tracker + record
26+
// Found a match, don't keep searching!
27+
if (subTotal === this.target) {
28+
results.push(record)
1629
return true
1730
}
31+
// When subtotal exceeds target, return immediately and don't waste time
32+
// on more loops that won't get results
33+
if (subTotal > this.target) {
34+
return false
35+
}
36+
// If we're already at max depth, don't waste time on more loops
37+
if (this.depth >= this.maxDepth) {
38+
return false
39+
}
40+
// Check the next level down
41+
const res = records.find(matcher, {
42+
maxDepth: this.maxDepth,
43+
target: this.target,
44+
depth: this.depth + 1,
45+
tracker: this.tracker + record
46+
})
47+
// Children matched, so record this one as well
48+
if (res) {
49+
results.push(record)
50+
return true
51+
}
52+
// Nothing found with this combo, step to the next sibling
1853
return false
19-
})
20-
if (matcher) {
21-
results.push(matcher)
2254
}
2355

56+
records.find(matcher, {
57+
maxDepth: goal,
58+
target: checksum
59+
})
60+
2461
if (results.length < 2) {
2562
throw new Error('Couldn\'t find a checksum match')
2663
}

0 commit comments

Comments
 (0)