13

I need to increment a string from.. let's say aaa to zzz and write every incrementation in the console (is incrementation even a word?). It would go something like this:

aaa
aab
aac
...
aaz

aba
abb
abc
...
abz

aca
acb

And so on. So far I have incremented a single letter by doing this:

String.prototype.replaceAt = function(index, character) {
    return this.substr(0, index) + character + this.substr(index+character.length);
}

string = "aaa";

string = string.replaceAt(2, String.fromCharCode(string.charCodeAt(2) + 1));

//string == "aab"

However, I am lost when it comes to the final letter being z and it should then increment letter 2 (index 1) and reset the last letter to be a.

Does anyone have or know a smart solution to this? Thanks!

MortenMoulder
  • 6,138
  • 11
  • 60
  • 116

12 Answers12

27

Treat the string like it's a base 36 number.

Convert it to decimal, add 1, convert back to base 36, and replace any zeroes with the letter 'a':

var str= 'aaa',
    s= str;

while(str!=='zzz') {
  str= ((parseInt(str, 36)+1).toString(36)).replace(/0/g,'a');
  s+= ' '+str;
}

document.body.innerHTML= s;
Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79
10

This function gives 3 characters based on a number:

function n2s (n) {
    var s = '';
    while (s.length < 3) {
        s = String.fromCharCode(97 + n % 26) + s;
        n = Math.floor(n / 26);
    }
    return s;
}

To print strings from "aaa" to "zzz":

var zzz = Math.pow(26, 3) - 1;
for (var n = 0; n <= zzz; n++) {
    console.log(n2s(n));
}

function n2s (n) {
    var s = '';
    while (s.length < 3) {
        s = String.fromCharCode(97 + n % 26) + s;
        n = Math.floor(n / 26);
    }
    return s;
}

var result = [];
var zzz = Math.pow(26, 3) - 1;
for (var n = 0; n <= zzz; n++) {
    result.push(n2s(n));
}
document.body.innerHTML = result.join(' ');

Ask for details :-)


Improvements

Performances compared to the accepted answer: http://jsperf.com/10-to-26.

// string to number: s2n("ba") -> 26
function s2n(s) {
    var pow, n = 0, i = 0;
    while (i++ < s.length) {
        pow = Math.pow(26, s.length - i);
        n += (s.charCodeAt(i - 1) - 97) * pow;
    }
    return n;
}

// number to string: n2s(26) -> "ba"
function n2s(n) {
    var s = '';
    if (!n) s = 'a'; 
    else while (n) {
        s = String.fromCharCode(97 + n % 26) + s;
        n = Math.floor(n / 26);
    }
    return s;
}

// pad("ba", 4) -> "aaba"
function pad (s, n) {
    while (s.length < n) s = 'a' + s;
    return s;
}

Usage:

var from = s2n('azx');
var to = s2n('baa');
for (var n = from; n <= to; n++) {
    console.log(pad(n2s(n), 3));
}

Output:

azx
azy
azz
baa

Recursivity

Probably less efficient in terms of memory use or computation time: https://jsperf.com/10-to-26/4.

function n2s(n) {
    var next = Math.floor(n / 26);
    return (
        next ? n2s(next) : ''
    ) + (
        String.fromCharCode(97 + n % 26)
    );
}

function s2n(s) {
    return s.length && (
        (s.charCodeAt(0) - 97)
    ) * (
        Math.pow(26, s.length - 1)
    ) + (
        s2n(s.slice(1))
    );
}
Community
  • 1
  • 1
  • 2
    That answer deserves more upvotes. It's more generic approach than in the accepted answer. Works for any alphabet after changing some hardcoded numbers. – grabantot Mar 27 '17 at 12:17
  • is it possible to get `aaa` in `n2s(n)` – Bouh Jul 21 '21 at 18:41
  • 1
    @bouh I don't think so since `a` stands for `0`, but you can do it using the `pad` function like so: `pad("", 3)`. –  Jul 22 '21 at 19:49
4

Took a bit of algorithmic approach. This function takes initial string as an argument, increments next possible char in alphabet and at last returns the result.

function generate(str)
{
  var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
  var chars = [];
  for(var i = 0; i < str.length; i++)
  {
    chars.push(alphabet.indexOf(str[i]));
  }
  for(var i = chars.length - 1; i >= 0 ; i--)
  {
    var tmp = chars[i];
    if(tmp >= 0 && tmp < 25) {
      chars[i]++;
      break;
    }
    else{chars[i] = 0;}
  }
  var newstr = '';
  for(var i = 0; i < chars.length; i++)
  {
    newstr += alphabet[chars[i]];
  }
  return newstr;
} 

Here is the loop helper function which accepts the initial string to loop through and generate all combinations.

function loop(init){
  var temp = init;
  document.write(init + "<br>");
  while(true)
  {
    temp = generate(temp);
    if(temp == init) break;
    document.write(temp + "<br>");
  }
}

Usage: loop("aaa");

CODEPEN

fsacer
  • 1,382
  • 1
  • 15
  • 23
2

I took a different approach with this, using a permutations function which recursively generated all the possible permutations one could generate using characters from an array repeated n times. The code looks like this.

//recursively generates permutations
var permutations = function (li, rep) {
    var i, j, next, ret = [];
    // base cases
    if (rep === 1) {
        return li;
    }
    if (rep <= 0) {
        return [];
    }
    // non-base case
    for (i = 0; i < li.length; i += 1) {
        // generate the next deepest permutation and add
        // the possible beginnings to those
        next = permutations(li, rep-1);
        for (j = 0; j < next.length; j += 1) {
            ret.push(li[i] + next[j]);
        }
    }
    return ret;
};

// returns an array of numbers from [start, end)
// range(10, 14) -> [10, 11, 12, 13]
var range = function (start, end) {
    var i, ret = [];
    for (i = start; i < end; i+= 1) {
        ret.push(i);
    }
    return ret;
};

// generates letters ('abcd...')
var letters = String.fromCharCode.apply(this, range('a'.charCodeAt(0), 'z'.charCodeAt(0)+1));

// calls the function itself, and .join's it into a string
document.body.innerHTML = (permutations(letters, 3)).join(' ');
umop aplsdn
  • 300
  • 1
  • 12
1

I used your code and added a few new functions.

String.prototype.replaceAt = function(index, character) {
    return this.substr(0, index) + character + this.substr(index+character.length);
}

String.prototype.incrementAt = function(index) {
    var newChar = String.fromCharCode(this.charCodeAt(index) + 1); // Get the next letter that this char will be
    if (newChar == "{") { // If it overflows
        return this.incrementAt(index - 1).replaceAt(index, "a"); // Then, increment the next character and replace current char with 'a'
    }
    return this.replaceAt(index, newChar); // Replace this char with next letter
}

String.prototype.increment = function() {
    return this.incrementAt(this.length - 1); // Starts the recursive function from the right
}

console.log("aaa".increment()); // Logs "aab"
console.log("aaz".increment()); // Logs "aba"
console.log("aba".increment()); // Logs "abb"
console.log("azz".increment()); // Logs "baa"

This incrementAt function is recursive and increments the character it is currently on. If in the process it overflows (the character becomes { which is after z) it calls incrementAt on the letter before the one it is on.

The one problem with this code is if you try to increment zzz you get aaaz. This is because it is trying to increment the -1th character which is the last one. If I get time later I'll update my answer with a fix.

Note that this solution will work if you have a different length string to start off. For example, "aaaa" will count up to "zzzz" just fine.

Will
  • 378
  • 7
  • 17
  • So how exactly would I go from aaa to zzz and do something with each string? Like if I wanted to console.log every way from aaa to zzz (or aa to zz to keep it more simple)? – MortenMoulder Jun 06 '15 at 18:28
  • So, you'd just say something like `myStr = "aa"; while (myStr!= "zz") { myStr = myStr.increment(); console.log(myStr); } ` this would give you aa through to zz – Will Jun 06 '15 at 18:44
1

Let's try this approach. It's a straight loop which produces the complete sequence from aaa,aab,aac,.....,xzz,yzz,zzz

function printSeq(seq){
    console.log(seq.map(String.fromCharCode).join(''));
}


var sequences = [];

(function runSequence(){
    var seq = 'aaa'.split('').map(function(s){return s.charCodeAt(0)});
    var stopCode = 'z'.charCodeAt(0);
    do{
        printSeq(seq);
        sequences.push(seq.map(String.fromCharCode).join(''));
        if (seq[2]!=stopCode) seq[2]++;
        else if (seq[1]!=stopCode) seq[1]++;
        else if (seq[0]!=stopCode) seq[0]++;
    }while (seq[0]<stopCode);
    printSeq(seq);
    sequences.push(seq.map(String.fromCharCode).join(''));
})();

The results are displayed in the console and also you'll get a complete sequence stored in sequence array. Hope this is readable and helpful.

TaoPR
  • 5,932
  • 3
  • 25
  • 35
  • Did you test the code? I go from aaa to aaz and the hell breaks loose. – MortenMoulder Jun 06 '15 at 18:47
  • Definitely, I re-run the snippet right before replying this and I've got a correct result. The output runs from aaa, aab, ... zzz. Have you run it from the StackOverflow snippet or copied it to run elsewhere? – TaoPR Jun 06 '15 at 18:52
  • I tried in JSFiddle. Thanks for the contribution, but I marked one as answer. – MortenMoulder Jun 06 '15 at 18:57
  • @Snorlax No worry. I love the answer you picked too. I just wanted to offer a different approach :) – TaoPR Jun 06 '15 at 18:59
1

I just want to provide an alternative answer to @procrastinator's (since I can't comment on the answer because I don't have enough points on Stackoverflow). His answer seems like the most generic approach but I can't help and notice that after "z" comes "ba" when op expect it to be "aa". Also, this follows how Excel name it's columns.

Here is the code with corrections:

function s2n(s) {
    var pow, n = 0, i = 0;
    while (i++ < s.length) {
        pow = Math.pow(26, s.length - i);
        var charCode = s.charCodeAt(i - 1) - 96;
        n += charCode * pow;
    }
    return n;
}

function n2s(n) {
    var s = '';  
    var reduce = false;

    if (n === undefined) {
        n = 0;
    } else {
        n--;
    }
    while (n !== undefined) {
        s = String.fromCharCode(97 + n % 26) + s;
        n = Math.floor(n / 26);
        if (n === 0) {
            n = undefined;
        } else {
            n--;
        }
    }
    return s;
}

Instead of starting from 0, this will consider 1 to be "a", 26 to be "z", 27 to be "aa" and so on.

0

Assuming you will always have 3 letters (or any other set number of letters), off the top of my head I would think to:

Have separate variables for each letter, so instead of:

string = "aaa";

Have:

string1 = "a";
string2 = "a";
string3 = "a";

Then increment the one you need at each iteration. This will take a little trial and error probably, and looks like you're going from the right over to the left, so roughly:

if(string3 != "z"){
    // Increment string 3 by a letter
}else if(string2 != "z"){
    // Increment string 2 by a letter
}else if (string1 != "z"){
    // Increment string 1 by a letter
}else{
    // What ever you want to do if "zzz"
}

I didn't test that but it would be something close.

Then

string = string1 + string2+ string3

Now you are left with a single variable like before which you can do what you intended with (i.e. output etc.)

You could also do this with a string array, which would make it easier to have a changing amount of letters, and would need a little more code to count the array length and stuff, but I'd want to get it working at least statically first like above.

0

The example below can work from a...a to z...z.

String.prototype.replaceAt = function(index, character) {
  return this.substr(0, index) + character + this.substr(index + character.length);
}

String.prototype.inc = function() {
  var stop = 'z';
  var start = 'a';
  var currentIndex = this.length - 1;
  var string = this.replaceAt(currentIndex, String.fromCharCode(this.charCodeAt(currentIndex) + 1));

  for (var i = string.length - 1; i > 0; i--) {
    if (string[i] == String.fromCharCode(stop.charCodeAt(0) + 1)) {
      string = string.replaceAt(i - 1, String.fromCharCode(string.charCodeAt(i - 1) + 1));
      string = string.replaceAt(i, String.fromCharCode(start.charCodeAt(0)));
    }
  }
  return string;
}

var string = "aaa";
var allStrings = string;
while(string != "zzz") {
  string = string.inc();
  allStrings += " " + string;
}
document.getElementById("current").innerHTML = allStrings;
<div id="current"></div>
adricadar
  • 9,971
  • 5
  • 33
  • 46
0

Interesting approach with Number#toString:

var n = 13330
var ns = []

for(var i = 0; i < 26; i++) {
  for(var j = 0; j < 26; j++) {
    for(var k = 0; k < 26; k++) {
      ns.push(n.toString(36))
      n++
    }
    n += 10 // jump from '(x)0' to '(x+1)a', etc.
  }
  n += 360 // jump from '(x)0a' to '(x)aa', etc.
}

console.log(ns) // the strings you wanted
Leo
  • 13,428
  • 5
  • 43
  • 61
0

This will function will do the part of incrementing the string to next sequence

function increment(str){

    var arr = str.split("");
    var c;
    for(var i=arr.length-1; i>=0; i--){
        c = (arr[i].charCodeAt(0)+1)%123;
        arr[i] = String.fromCharCode(c==0?97:c);
        if(c!=0)break;
    }
return arr.join("");
}

I was working on another solution to increment by any number and also in reverse direction. The code still has some bugs, but just putting it up here to receive some suggestions. pass in negative numbers to go in reverse direction. Code fails for some edge cases, for eg: when character is 'a' and num is negative number

function jumpTo(str,num){

    var arr = str.split("");
    var c;
    for(var i=arr.length-1; i>=0; i--){
        c = (arr[i].charCodeAt(0)+1)%123;
        c += c==0?97+num-1:num-1;
        arr[i] = String.fromCharCode(c==0?97:c);
        if(c!=0)break;
    }
return arr.join("");
}
Himanshu Tanwar
  • 906
  • 6
  • 18
0

Gets A-Z, AA-ZZ, AAA-ZZZ etc. until the number of cycles is up.

function createList(maxCycles) {
  if (typeof maxCycles != "number") {
    console.log("number expected");
    return;
  }

  const alphaLen = 26;
  const alpha = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];

  let list = [alpha];

  // go through all cycles
  for (let cycleNo = 1; cycleNo < maxCycles; cycleNo++) {
    list[cycleNo] = [];
    pastCollection = list[cycleNo - 1];
    pastLen = pastCollection.length;

    for (let i = 0; i < pastLen; i++) {
      for (let j = 0; j < alphaLen; j++) {
        // use past item then add a letter of the alphabet at the end
        list[cycleNo].push(pastCollection[i] + alpha[j]);
      }
    }
  }

  return list;
}

(function(maxCycles) {
  console.log(createList(maxCycles));
})(3);