Skip to content

Commit 28efcb7

Browse files
feat(2023-01): don't use string replacement to parse text
"eightwo" should render as "82" not "8wo" Solves part 2
1 parent cf077d8 commit 28efcb7

File tree

3 files changed

+187
-39
lines changed

3 files changed

+187
-39
lines changed

2023/day-01/checksum.js

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,104 @@ const checksumLine = (data) => {
1414
return parseInt(result)
1515
}
1616

17+
const lazyNums = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
18+
const lazyReg = new RegExp(lazyNums.join('|'))
19+
const lazyNumsReversed = lazyNums.map((num) => num.split('').reverse().join(''))
20+
const lazyReversReg = new RegExp(lazyNumsReversed.join('|'))
21+
22+
const lookupPosition = (match) => {
23+
const idx = lazyNums.indexOf(match)
24+
if (idx > 9) {
25+
return idx - 9
26+
}
27+
return idx
28+
}
29+
30+
const lookupPositionReversed = (match) => {
31+
const reverseMatch = match.split('').reverse().join('')
32+
const idx = lazyNums.indexOf(reverseMatch)
33+
if (idx > 9) {
34+
return idx - 9
35+
}
36+
return idx
37+
}
38+
39+
const lazyChecksumLine = (data) => {
40+
let first = ''
41+
data.replace(lazyReg, (match) => {
42+
first = lookupPosition(match)
43+
return match // reinsert so we don't bork the data string
44+
})
45+
// find last matching digit by reversing the string and searching backwards
46+
let last = ''
47+
data = data.split('').reverse().join('')
48+
data.replace(lazyReversReg, (match) => {
49+
last = lookupPositionReversed(match)
50+
return match // reinsert so we don't bork the data string
51+
})
52+
53+
return parseInt(`${first}${last}`)
54+
}
55+
1756
/**
1857
* Generates the checksum for an entire set
1958
* @param {array} set of lines containing data
2059
*/
21-
const checksumSet = (set) => {
60+
const checksumSet = (set, sanitize = false, lazy = false) => {
61+
let filter = (data) => data
62+
if (sanitize) {
63+
filter = sanitizeLine
64+
}
65+
66+
let checksum = checksumLine
67+
if (lazy) {
68+
checksum = lazyChecksumLine
69+
}
70+
2271
return set.reduce((total, current) => {
23-
return total + checksumLine((current))
72+
return total + checksum(
73+
filter(current)
74+
)
2475
}, 0)
2576
}
2677

27-
/**
28-
* Generates the checksum for an entire set when data is not sanitized
29-
* @param {array} set of lines containing data
30-
*/
31-
const checksumUnSanitizedSet = (set) => {
32-
return set.reduce((total, current) => {
33-
return total + checksumLine(sanitizeLine(current))
34-
}, 0)
78+
const numbers = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
79+
const numbersReversed = numbers.map((num) => num.split('').reverse().join(''))
80+
const reg = new RegExp(numbers.join('|'))
81+
const regGlobal = new RegExp(numbers.join('|'), 'g')
82+
const regReversed = new RegExp(numbersReversed.join('|'))
83+
84+
// Sanitizes using a single-pass regex replace all
85+
// Produces 53885 which is incorrect for part 2
86+
const byRegex = (data) => {
87+
return data.replaceAll(regGlobal, (matched) => numbers.indexOf(matched) + 1)
88+
}
89+
90+
// Sanitizes by replacing just the first and last text digits, in case shared letters is supposed to work
91+
// Produces 53853 which is too low for part 2
92+
const byFirstLast = (data) => {
93+
// sanitize first matching digit
94+
data = data.replace(reg, (matched) => numbers.indexOf(matched) + 1)
95+
// sanitize last matching digit by reversing the string and searching backwards
96+
data = data.split('').reverse().join('')
97+
data = data.replace(regReversed, (matched) => numbersReversed.indexOf(matched) + 1)
98+
99+
// return original order
100+
return data.split('').reverse().join('')
35101
}
36102

37-
const numbers = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
38-
const reg = new RegExp(numbers.join('|'), 'g')
39103
/**
40104
* Sanitzizes a line by replacing spelled-out numbers with data
41105
* @param {string} data line of input to sanitize
42106
*/
43-
const sanitizeLine = (data) => {
44-
return data.replaceAll(reg, (matched) => numbers.indexOf(matched))
107+
const sanitizeLine = (data, method = 'byFirstLast') => {
108+
const methods = {
109+
none: (data) => data,
110+
byFirstLast,
111+
byRegex
112+
}
113+
114+
return methods[method](data)
45115
}
46116

47-
module.exports = { checksumLine, checksumSet, checksumUnSanitizedSet, sanitizeLine }
117+
module.exports = { checksumLine, checksumSet, sanitizeLine, lazyChecksumLine }

2023/day-01/checksum.test.js

Lines changed: 101 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-env mocha */
22
const { expect } = require('chai')
3-
const { checksumSet, checksumUnSanitizedSet, checksumLine, sanitizeLine } = require('./checksum')
3+
const { checksumSet, checksumLine, sanitizeLine, lazyChecksumLine } = require('./checksum')
44
const fs = require('fs')
55
const path = require('path')
66
const filePath = path.join(__dirname, 'input.txt')
@@ -30,7 +30,7 @@ describe('--- Day 1: Trebuchet?! ---', () => {
3030
})
3131
describe('Part 2', () => {
3232
describe('sanitizeLine', () => {
33-
const set = [
33+
const data = [
3434
'two1nine',
3535
'eightwothree',
3636
'abcone2threexyz',
@@ -41,40 +41,119 @@ describe('--- Day 1: Trebuchet?! ---', () => {
4141
]
4242
const result = [29, 83, 13, 24, 42, 14, 76]
4343
it('cleans up a string when digits are spelled out', () => {
44+
const set = JSON.parse(JSON.stringify(data))
4445
for (let x = 0; x < set.length; x++) {
4546
expect(checksumLine(sanitizeLine(set[x]))).to.equal(result[x])
47+
// expect(checksumLine(sanitizeLine(set[x], 'sanitizeByRegex'))).to.equal(result[x])
48+
// expect(checksumLine(sanitizeLine(set[x], 'sanitizeFirstLast'))).to.equal(result[x])
4649
}
4750
})
48-
it('handles first matches, and doesn\'t allow for multiple words to share letters', () => {
49-
expect(sanitizeLine('eightwothree')).to.equal('8wo3')
51+
it('allows for skipping sanitation', () => {
52+
const set = JSON.parse(JSON.stringify(data))
53+
for (let x = 0; x < set.length; x++) {
54+
expect(sanitizeLine(set[x], 'none')).to.equal(data[x])
55+
}
5056
})
5157
})
52-
describe('checksumUnSanitizedSet', () => {
53-
it('calculates the checksum for a set of lines by summing the checksum of each sanitized line', () => {
54-
const set = [
55-
'two1nine',
56-
'eightwothree',
57-
'abcone2threexyz',
58-
'xtwone3four',
59-
'4nineeightseven2',
60-
'zoneight234',
61-
'7pqrstsixteen'
62-
]
63-
expect(checksumUnSanitizedSet(set)).to.equal(281)
58+
describe('checksumSet', () => {
59+
const data = [
60+
'two1nine',
61+
'eightwothree',
62+
'abcone2threexyz',
63+
'xtwone3four',
64+
'4nineeightseven2',
65+
'zoneight234',
66+
'7pqrstsixteen'
67+
]
68+
it('can sanitize', () => {
69+
expect(checksumSet(data, true)).to.equal(281)
6470
})
6571
})
72+
describe('lazyChecksumLine', () => {
73+
const data = [
74+
'two1nine',
75+
'eightwothree',
76+
'abcone2threexyz',
77+
'xtwone3four',
78+
'4nineeightseven2',
79+
'zoneight234',
80+
'7pqrstsixteen'
81+
]
82+
const result = [29, 83, 13, 24, 42, 14, 76]
83+
it('can match text or numeric for checksum calcs', () => {
84+
const set = JSON.parse(JSON.stringify(data))
85+
for (let x = 0; x < set.length; x++) {
86+
expect(lazyChecksumLine(set[x])).to.equal(result[x])
87+
}
88+
})
89+
})
90+
91+
// it('doesn`t sanitize by default', () => {
92+
// const set = [
93+
// 'two1nine',
94+
// 'eightwothree',
95+
// 'abcone2threexyz',
96+
// 'xtwone3four',
97+
// '4nineeightseven2',
98+
// 'zoneight234',
99+
// '7pqrstsixteen'
100+
// ]
101+
// expect(checksumSet(set)).to.be.NaN
102+
// })
103+
// it('allows for sanitizing to be explicitly disabled', () => {
104+
// const set = [
105+
// 'two1nine',
106+
// 'eightwothree',
107+
// 'abcone2threexyz',
108+
// 'xtwone3four',
109+
// '4nineeightseven2',
110+
// 'zoneight234',
111+
// '7pqrstsixteen'
112+
// ]
113+
// expect(checksumSet(set, 'none')).to.be.NaN
114+
// })
115+
// // it('calculates the checksum for a set of lines by summing the checksum of each line', () => {
116+
// // const set = [
117+
// // 'two1nine',
118+
// // 'eightwothree',
119+
// // 'abcone2threexyz',
120+
// // 'xtwone3four',
121+
// // '4nineeightseven2',
122+
// // 'zoneight234',
123+
// // '7pqrstsixteen'
124+
// // ]
125+
// // expect(checksumSet(set)).to.equal(281)
126+
// // })
127+
// })
66128
describe('integeration', () => {
67-
it('53853 is too low for part 2', (done) => {
68-
let data
69-
fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => {
129+
let initData
130+
before((done) => {
131+
fs.readFile(filePath, { encoding: 'utf8' }, (err, rawData) => {
70132
if (err) throw err
71-
initData = inputToArray(initData.trim())
133+
initData = inputToArray(rawData.trim())
72134
// Deep copy to ensure we aren't mutating the original data
73-
data = JSON.parse(JSON.stringify(initData))
74-
expect(checksumUnSanitizedSet(data)).to.be.greaterThan(53853)
135+
// data = JSON.parse(JSON.stringify(initData))
75136
done()
76137
})
77138
})
139+
140+
it('is not done without sanitation, since that matches part 1', () => {
141+
const data = JSON.parse(JSON.stringify(initData))
142+
const result = checksumSet(data, true)
143+
expect(result).to.not.equal(54953)
144+
})
145+
146+
it('is not done with a one-time regex', () => {
147+
const data = JSON.parse(JSON.stringify(initData))
148+
const result = checksumSet(data, true)
149+
expect(result).to.not.equal(53885) // result of one-time regex
150+
})
151+
152+
it('is not done by sanitizing just the first and last strings', () => {
153+
const data = JSON.parse(JSON.stringify(initData))
154+
const result = checksumSet(data, true)
155+
expect(result).to.not.equal(53853) // result of first/last substitution onlye
156+
})
78157
})
79158
})
80159
})

2023/day-01/solution.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => {
2121

2222
const part2 = () => {
2323
const data = resetInput()
24-
console.debug(data)
25-
return 'No answer yet'
24+
return checksumSet(data, false, true)
2625
}
2726
const answers = []
2827
answers.push(part1())

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy