Skip to content

Commit d1dcef1

Browse files
chieveitjkirschner
authored andcommitted
fix alphabet indexAt and stringAt
1 parent 549f5cf commit d1dcef1

File tree

2 files changed

+32
-46
lines changed

2 files changed

+32
-46
lines changed

src/core/alphabet.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ const alphabets = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', '
1212
export function stringAt(index) {
1313
let str = '';
1414
let cindex = index;
15-
while (cindex >= alphabets.length) {
15+
// Each loop iteration converts a base-26 (alphabet length) digit to a
16+
// character [A-Z]. Because it works from least to most significant digit,
17+
// each new character must be added to the FRONT of the string being built.
18+
do {
19+
str = alphabets[parseInt(cindex, 10) % alphabets.length] + str;
1620
cindex /= alphabets.length;
1721
cindex -= 1;
18-
str += alphabets[parseInt(cindex, 10) % alphabets.length];
19-
}
20-
const last = index % alphabets.length;
21-
str += alphabets[last];
22+
} while (cindex >= 0);
2223
return str;
2324
}
2425

@@ -30,12 +31,21 @@ export function stringAt(index) {
3031
*/
3132
export function indexAt(str) {
3233
let ret = 0;
33-
for (let i = 0; i < str.length - 1; i += 1) {
34+
// Each loop iteration converts a digit from a base-26 [A-Z] string to a
35+
// base 10 integer, working from most to least significant base-26 digit.
36+
for (let i = 0; i < str.length; i += 1) {
37+
// Calculate offset from 'A', which has character code 65
3438
const cindex = str.charCodeAt(i) - 65;
3539
const exponet = str.length - 1 - i;
36-
ret += (alphabets.length ** exponet) + (alphabets.length * cindex);
40+
// 'A' is interpreted as 0 when in the 0th digit, but as 1 when in any
41+
// other digit. Therefore, we will increment cindex so that [0-25] is
42+
// remapped to [1-26] for all digits, then decrement the result after the
43+
// loop completes by 1 to remap the 0th digit back to [0-25]. For example,
44+
// 'AA' will be converted to 11 by the loop, then decremented to 10 before
45+
// being returned.
46+
ret += (cindex + 1) * (alphabets.length ** exponet);
3747
}
38-
ret += str.charCodeAt(str.length - 1) - 65;
48+
ret -= 1;
3949
return ret;
4050
}
4151

test/core/alphabet_test.js

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,54 +11,30 @@ import {
1111
describe('alphabet', () => {
1212
describe('.indexAt()', () => {
1313
it('should return 0 when the value is A', () => {
14-
assert.equal(indexAt('A'), 0);
14+
assert.equal(indexAt('A'), 1 * 26 ** 0 - 1);
1515
});
16-
it('should return 25 when the value is Z', () => {
17-
assert.equal(indexAt('Z'), 25);
16+
it('should return 27 when the value is AB', () => {
17+
assert.equal(indexAt('AB'), 1 * 26 ** 1 + 2 * 26 ** 0 - 1);
1818
});
19-
it('should return 26 when the value is AA', () => {
20-
assert.equal(indexAt('AA'), 26);
19+
it('should return 730 when the value is ABC', () => {
20+
assert.equal(indexAt('ABC'), 1 * 26 ** 2 + 2 * 26 ** 1 + 3 * 26 ** 0 - 1);
2121
});
22-
it('should return 52 when the value is BA', () => {
23-
assert.equal(indexAt('BA'), 52);
24-
});
25-
it('should return 54 when the value is BC', () => {
26-
assert.equal(indexAt('BC'), 54);
27-
});
28-
it('should return 78 when the value is CA', () => {
29-
assert.equal(indexAt('CA'), 78);
30-
});
31-
it('should return 26 * 26 when the value is ZA', () => {
32-
assert.equal(indexAt('ZA'), 26 * 26);
33-
});
34-
it('should return 26 * 26 + 26 when the value is AAA', () => {
35-
assert.equal(indexAt('AAA'), (26 * 26) + 26);
22+
it('should return 19009 when the value is ABCD', () => {
23+
assert.equal(indexAt('ABCD'), 1 * 26 ** 3 + 2 * 26 ** 2 + 3 * 26 ** 1 + 4 * 26 ** 0 - 1);
3624
});
3725
});
3826
describe('.stringAt()', () => {
3927
it('should return A when the value is 0', () => {
40-
assert.equal(stringAt(0), 'A');
41-
});
42-
it('should return Z when the value is 25', () => {
43-
assert.equal(stringAt(25), 'Z');
44-
});
45-
it('should return AA when the value is 26', () => {
46-
assert.equal(stringAt(26), 'AA');
47-
});
48-
it('should return BC when the value is 54', () => {
49-
assert.equal(stringAt(54), 'BC');
50-
});
51-
it('should return CB when the value is 78', () => {
52-
assert.equal(stringAt(78), 'CA');
28+
assert.equal(stringAt(1 * 26 ** 0 - 1), 'A');
5329
});
54-
it('should return ZA when the value is 26 * 26', () => {
55-
assert.equal(stringAt(26 * 26), 'ZA');
30+
it('should return AB when the value is 27', () => {
31+
assert.equal(stringAt(1 * 26 ** 1 + 2 * 26 ** 0 - 1), 'AB');
5632
});
57-
it('should return Z when the value is 26 * 26 + 1', () => {
58-
assert.equal(stringAt((26 * 26) + 1), 'ZB');
33+
it('should return ABC when the value is 730', () => {
34+
assert.equal(stringAt(1 * 26 ** 2 + 2 * 26 ** 1 + 3 * 26 ** 0 - 1), 'ABC');
5935
});
60-
it('should return AAA when the value is 26 * 26 + 26', () => {
61-
assert.equal(stringAt((26 * 26) + 26), 'AAA');
36+
it('should return ABCD when the value is 19009', () => {
37+
assert.equal(stringAt(1 * 26 ** 3 + 2 * 26 ** 2 + 3 * 26 ** 1 + 4 * 26 ** 0 - 1), 'ABCD');
6238
});
6339
});
6440
describe('.expr2xy()', () => {

0 commit comments

Comments
 (0)