Link to home
Start Free TrialLog in
Avatar of jxbma
jxbmaFlag for United States of America

asked on

What is a good method for incrementing a string sequence from "A" to "ZZZZ" in C# or vb.Net?

Hi:

I have an interesting problem I need to solve.
I'm sure that someone has encountered this somewhere
and that an elegant solution exists.

I'm trying to write an method which increments and returns the next
alphanumeric character in a sequence.

These letters are intended to range from 'A' to 'ZZZZ'.
I am only dealing with capital (uppercase letters)

Consider the following examples:

    INPUT        OUTPUT
    ===================
    A            B
    G            H
    Z            AA
    AZ          BA
    AZZZ      BAAA

Open in new window


I'm looking for a solution which is more or less portable across
languages (C#, VB.Net, JavaScript/JQuery). This of course rules out
trying something with LINQ.

I've made several attempts at this, but the all are kind of klunky.
Any thoughts, ideas, suggestions or directions to explore would be greatly appreciated.

Thanks,
JohnB
Avatar of Ryan Chong
Ryan Chong
Flag of Singapore image

just a quick guess... you probably need to check the ascii of the input char, then you need that ascii + 1
I would try to map A, B, ..., Z to 0, 1, ..., 25, and then use base 26 to get the next (output) value.
Avatar of Dr. Klahn
Dr. Klahn

Modulo 26 counters numbering the length of the input string with each counter carrying to the next.  In the special case of a carry out of the high counter, the output string becomes "A" repeated (length of input string +1) times.  Works no matter how long the string is.

Input        Counters        Successor          Output
AA           00  00          00   01             AB
AZ           00  25          01   00+C           BA
ABB          00  01  01      00   01   02        ABC
BZZ          01  25  25      02   00+C 00+C      CAA
ZZZ          25  25  25      00+C 00+C 00+C      AAAA

Open in new window

You piqued my interest. I don't know how this would be done in C# or VB.NET. But in javascript, I believe the following would work:
function incrementString(input) {
    // create an array for holding all the letters, and
    // a boolean for if the string has been incremeneted
    var inputArray = [],
        inced = false;
    // work through the string backward
    for (var i = input.length-1; i>-1; i--) {
        if (inced === false) {      // if a character has yet to be incremented
            if (input.charAt(i) !== 'Z') {      // if the character is NOT a Z
                // insert into the beginning of the array the next
                // ASCII character after the current character
                inputArray.unshift(String.fromCharCode(input.charCodeAt(i)+1) );
                // the string has been incremented
                inced = true;
            } else {
                // if the character IS a Z, insert an A into the beginning
                // of the array and increment the next character
                inputArray.unshift('A');
            }
        } else {
            // if the string has already been incremented, just insert
            // the current character into the beginning of the array
            inputArray.unshift(input.charAt(i) );
        }
    }
    // if the string still has not been incremented, add an A to the string
    if (inced === false) {
        inputArray.unshift('A');
    }
    // join the characters in the array and return the joined string
    return inputArray.join('');
}
console.log(incrementString('ABZ') );
// should return 'ACA'

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Gustav Brock
Gustav Brock
Flag of Denmark image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Not sure if it fits author requirements but it can be done in few lines in C#:

        private static string increaseChars(string input)
        {
            List<char> outChars= new  List<char>();
            foreach (char ch in input.ToCharArray())
            {
                int newCharCode = ((int)ch<90)? ch+1 : 65;
                outChars.Add((char) newCharCode);
            }
            return new string(outChars.ToArray());
        }

Open in new window


=========================================

You may test it in the following Console application:

    class Program
    {
        static void Main(string[] args)
        {
            string input = "ABCD";
            Console.WriteLine(input);
            Console.WriteLine(increaseChars(input));
            Console.WriteLine("==============================");
            input = "AZZZ";
            Console.WriteLine(input);
            Console.WriteLine(increaseChars(input));
            Console.WriteLine("==============================");
            input = "XYZXYZ";
            Console.WriteLine(input);
            Console.WriteLine(increaseChars(input));
            Console.WriteLine("==============================");
            Console.ReadLine();
        }
        private static string increaseChars(string input)
        {
            List<char> outChars= new  List<char>();
            foreach (char ch in input.ToCharArray())
            {
                int newCharCode = ((int)ch<90)? ch+1 : 65;
                outChars.Add((char) newCharCode);
            }
            return new string(outChars.ToArray());
        }
    }

Open in new window

No, it fails for a string like ZZZ which should output as AAAA but becomes AAA only.

/gustav
Wow, didn't notice the requirement. A bit strange one...

OK, just forget. Didn't read carefully ... :)
Well, basically this is just a base 26 number system with members represented by letters from A to Z.

/gustav
No, it fails for a string like ZZZ which should output as AAAA but becomes AAA only.

Reaslly, someone is wrong, either author or you.  Or me :). If ZZZ should output as AAAA then AZ (see author's examples) should output as BAA. Or do I miss something?
Yes, a new letter, A, is only prefixed (as a carryover) when a leading Z will be raised by 1.

/gustav
Can be something like:

        private static string increaseChars(string input)
        {
            char[] outChars= new  char[input.Length];
            char[] inputChars = input.ToCharArray();
            for (int i = 0; i < input.Length; i++)
            {
                int newCharCode = (inputChars[i] < 90) ? inputChars[i]+1 : 65;
                outChars[i] = (char)newCharCode;
            }
            string result = inputChars[0] == 90 ? 'A' + (new string(outChars)) : new string(outChars);
            if (result.Length>4) { result = result.Substring(0, 4); }// if you want to restrict by 4 chars
            return result;
        }

Open in new window


Testing:

            string input = "ABCD";
            Console.WriteLine(input);
            Console.WriteLine(increaseChars(input));
            Console.WriteLine("==============================");
            input = "AZZZ";
            Console.WriteLine(input);
            Console.WriteLine(increaseChars(input));
            Console.WriteLine("==============================");
            input = "ZZZ";
            Console.WriteLine(input);
            Console.WriteLine(increaseChars(input));
            Console.WriteLine("==============================");
            input = "Z";
            Console.WriteLine(input);
            Console.WriteLine(increaseChars(input));
            Console.WriteLine("==============================");
            input = "ZAB";
            Console.WriteLine(input);
            Console.WriteLine(increaseChars(input));
            Console.WriteLine("==============================");
            input = "ZZZZ";
            Console.WriteLine(input);
            Console.WriteLine(increaseChars(input));
            Console.WriteLine("==============================");
            Console.ReadLine();

Open in new window

Private Function convertBase(input As Integer, radix As Integer) As String
    Dim result = ""
    Dim a = Asc("A")
    Do While input > -1
        Dim x = input Mod radix
        result = Chr(a + x) & result
        input = Math.Floor(input / radix) - 1
    Loop
    Return result
End Function

Private Function parseBase(input As String, radix As Integer) As Integer
    Dim length = input.Length
    Dim a = Asc("A")
    Dim result As Integer
    For Each c As Char In input
        Dim x  = Asc(c) - a + 1
        result += x * Math.Pow(radix, length - 1)
        length -= 1
    Next
    Return result - 1
End Function

Open in new window

Using:
For i = 0 To 5000
    Debug.Print(i & "; " & convertBase(i, 26))
Next

Open in new window

Dim x = parseBase("AZZZ", 26)
Debug.Print convertBase(x + 1, 26) 'BAAA

Open in new window

PS. To validate input string in parseBase function, add
 input = input.ToUpper
If Not System.Text.RegularExpressions.Regex.IsMatch(input, "[A-Z]") Then
    Throw New ArgumentException("Only letters A to Z allowed.", "input")
    Return -1
End If

Open in new window

C#
private string convertBase(int input, int radix)
{
    string result = "";
    int a = Convert.ToInt32('A');
    while (input > -1)
    {
        int x = input % radix;
        result = Convert.ToChar(a + x) + result;
        input = (int)Math.Floor((double)(input / radix)) - 1;
    }
    return result;
}

private int parseBase(string input, int radix)
{
    input = input.ToUpper();
    if (!System.Text.RegularExpressions.Regex.IsMatch(input, "[A-Z]"))
    {
        throw new ArgumentException("Only letters A to Z allowed.", "input");
    }
    int length = input.Length;
    int a = Convert.ToInt32('A');
    int result = 0;
    foreach (char c in input)
    {
        int x = Convert.ToInt32(c) - a + 1;
        result += x * (int)Math.Pow(radix, length - 1);
        length -= 1;
     }
     return result - 1;
}

Open in new window

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of jxbma

ASKER

Wow. Thanks (all) for all the excellent comments, suggestions and samples.
I had no idea that this would cause such a whirlwind of activity.
Awesome.