-1

I want to be able to count in base 26, but only with the letters of the alphabet.

I can cover the basics like A + 1 = B and Z + 1 = AA, but i want it to work for very long "numbers" like AZZEBERBZZ

Currently i have the following code in JavaScript

function getNextColumn(currentColumn) {
    currentColumn = currentColumn.toUpperCase();
    let lastLetterCode = currentColumn.charCodeAt(currentColumn.length - 1);

    if(lastLetterCode < 90) {
        return currentColumn.slice(0, -1) + String.fromCharCode(lastLetterCode + 1);
    } else {
        return currentColumn.slice(0, -1) + "A";
    }
}

But the issue here is that when i'm at AZ it just returns AAA instead of BA

How can i solve this?

EXTRA CONTEXT:

I need the function getNextColumn because I use this function to loop over an object created from an excel sheet, where to columns are counted in base26 but only with letters and no numbers

eisbehr
  • 12,243
  • 7
  • 38
  • 63
JustAnotherDev
  • 445
  • 1
  • 4
  • 20

2 Answers2

1

Basically you could use one function to get a numerical value and another to convert the numerical value back to the wanted format. This allows to make arithmetic operations.

For getting a number, you could use parseInt with base 36 and a correction of 9 (this gets only the value of letters) for the value and Array#reduce for getting the whole number of letters.

The factor of 26 is the length of the alphabet and a letter more left has a place value of times 26.

For geting a converted value back, you could use toString with base 36 for conversion to the wanted letters.

function getValue(s) {
    return s.split('').reduce((r, a) => r * 26 + parseInt(a, 36) - 9, 0) - 1;
}

function setValue(n) {
    var result = '';
    do {
        result = (n % 26 + 10).toString(36) + result;
        n = Math.floor(n / 26) - 1;
    } while (n >= 0)
    return result.toUpperCase();
}

console.log(getValue('A'));              //    0
console.log(setValue(getValue('A')));
console.log(getValue('B'));              //    1
console.log(setValue(getValue('B')));
console.log(getValue('Z'));              //   25
console.log(setValue(getValue('Z')));
console.log(getValue('AA'));             //   26
console.log(setValue(getValue('AA')));
console.log(getValue('AZ'));             //   51
console.log(setValue(getValue('AZ')));
console.log(getValue('CZ'));             //  103
console.log(setValue(getValue('CZ')));
console.log(getValue('ZZ'));             //  701
console.log(setValue(getValue('ZZ')));
console.log(getValue('DXH'));            // 3335
console.log(setValue(getValue('DXH')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Nice answer! Like mentioned in the comments of my question, using some higher order functions is always prefered because those are highly optimized! (read this on this SO question: https://stackoverflow.com/questions/2651112/is-recursion-ever-faster-than-looping?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) – JustAnotherDev Jun 01 '18 at 08:49
0

This is a perfect candidate to use a recursive function.

With a recursive function you need to define a base situation and a recursive situation.

You already covered the base situations, which are 2 situations in this case:

  • The input ends with some other letter than "Z"
  • The input is "Z"

In all other cases you have the "recursive situation" where your input:

  • Ends with a "Z"
  • and it's longer than 1 letter in total

For this "recursive situation" you can trim the last "Z" (because you need to replace it with an "A" anyway) and then recall this function again, like so:

function getNextColumn(currentColumn) {
    currentColumn = currentColumn.toUpperCase();
    let lastLetterCode = currentColumn.charCodeAt(currentColumn.length - 1);

    if(currentColumn === "Z"){
        return "AA";
    }

    if(lastLetterCode < 90) {
        return currentColumn.slice(0, -1) + String.fromCharCode(lastLetterCode + 1);
    }

    return getNextColumn(currentColumn.slice(0, -1)) + "A";
}
JustAnotherDev
  • 445
  • 1
  • 4
  • 20
  • 3
    Did you reply to your own question? – bugs Jun 01 '18 at 07:48
  • 1
    @bugs the interesting part is the time difference between question and answer, which implies the answer was already completely typed out the moment the question was posted. – ASDFGerte Jun 01 '18 at 07:54
  • Well, that is absolutely okay in general and wished for SO! ;) https://stackoverflow.com/help/self-answer @bugs – eisbehr Jun 01 '18 at 08:22
  • 1
    @eisbehr well, if I post a question and later on find a solution to my own problem, it's absolutely fine to post it as an answer. However, this doesn't seem to be the case for the OP, which posted a perfectly crafted answer 10 seconds after opening the question, without mentioning that they were the OP themselves. I'm not implying anything bad, just... werid. – bugs Jun 01 '18 at 08:24
  • Yes, i've answered my own question because i want to share my knowledge so far, and perhaps i can get some more improvements. I think that also the goal of SO :) And looking at the comments on my answer, i see that i can improve! Ye! – JustAnotherDev Jun 01 '18 at 08:25
  • @bugs That is completely okay too. SO is meant as knowledge base and you can write and answer your question instantly, just to share it. Completely fine! For more information about this please refer on meta. – eisbehr Jun 01 '18 at 08:28