Increment a string with letters?

拜拜、爱过 提交于 2019-12-03 06:22:22

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

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

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

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

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.

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.

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.

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.

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

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>

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("");
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!