MortenMoulder
MortenMoulder

Reputation: 6648

Increment a string with letters?

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!

Upvotes: 13

Views: 21892

Answers (12)

user13378972
user13378972

Reputation: 1

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);

Upvotes: 0

generalspoonful
generalspoonful

Reputation: 81

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.

Upvotes: 1

user1636522
user1636522

Reputation:

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))
    );
}

Upvotes: 10

Will
Will

Reputation: 378

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.

Upvotes: 1

umop aplsdn
umop aplsdn

Reputation: 290

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(' ');

Upvotes: 2

Himanshu Tanwar
Himanshu Tanwar

Reputation: 906

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("");
}

Upvotes: 0

adricadar
adricadar

Reputation: 10209

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>

Upvotes: 0

Leo
Leo

Reputation: 13828

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

Upvotes: 0

fsacer
fsacer

Reputation: 1402

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

Upvotes: 4

TaoPR
TaoPR

Reputation: 6052

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.

Upvotes: 1

Rick Hitchcock
Rick Hitchcock

Reputation: 35670

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;

Upvotes: 27

Darryl Jackman
Darryl Jackman

Reputation: 126

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.

Upvotes: 0

Related Questions