|
2 | 2 | /**
|
3 | 3 | * Validates a list of records by comparing every combination
|
4 | 4 | * 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 |
5 | 8 | */
|
6 |
| -const validateRecords = (records, checksum = 2020) => { |
| 9 | +const validateRecords = (records, checksum = 2020, goal = 2) => { |
7 | 10 | const results = []
|
8 | 11 |
|
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) |
16 | 29 | return true
|
17 | 30 | }
|
| 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 |
18 | 53 | return false
|
19 |
| - }) |
20 |
| - if (matcher) { |
21 |
| - results.push(matcher) |
22 | 54 | }
|
23 | 55 |
|
| 56 | + records.find(matcher, { |
| 57 | + maxDepth: goal, |
| 58 | + target: checksum |
| 59 | + }) |
| 60 | + |
24 | 61 | if (results.length < 2) {
|
25 | 62 | throw new Error('Couldn\'t find a checksum match')
|
26 | 63 | }
|
|
0 commit comments