• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 345
  • Last Modified:

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
0
jxbma
Asked:
jxbma
  • 4
  • 4
  • 4
  • +5
2 Solutions
 
Ryan ChongCommented:
just a quick guess... you probably need to check the ascii of the input char, then you need that ascii + 1
0
 
Duy PhamFreelance IT ConsultantCommented:
I would try to map A, B, ..., Z to 0, 1, ..., 25, and then use base 26 to get the next (output) value.
0
 
Dr. KlahnPrincipal Software EngineerCommented:
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

0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
Kim WalkerWeb Programmer/TechnicianCommented:
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

0
 
Gustav BrockCIOCommented:
For the fun, you could use LINQ for this:
string input = string.Empty;
string output = string.Empty;

input = "zazzz";

char charFirst = 'A';
char charLast = 'Z';
char charNull= (char)(((int)charFirst)-1);
int numberBase = 1 + (int)charLast - (int)charFirst;

IEnumerable<int> chars = (charNull + input).ToUpper().Select(x => (int)x - (int)charNull);
int carryOver = 0;
int increment = 1;
for (int i = chars.Count<int>(); i > 0; i--)
{
	int ascii = chars.ElementAt<int>(i - 1) + carryOver + increment;
	carryOver=(ascii - 1) / numberBase;
	ascii= (ascii - 1) % numberBase + 1;
	increment = 0;
	if (ascii > 0)
	{
		output = ((char)((int)charNull + ascii)).ToString() + output;
	}
}
Console.WriteLine(input.ToUpper());
Console.WriteLine(output);

Open in new window


Result:
ZAZZZ
ZBAAA

Open in new window


To not use LINQ, replace the IEnumerable with an array:
string input = string.Empty;
string output = string.Empty;

input = "zazzz";

char charFirst = 'A';
char charLast = 'Z';
char charNull = (char)(((int)charFirst) - 1);
int numberBase = 1 + (int)charLast - (int)charFirst;

char[] chars = (charNull + input.ToUpper()).ToCharArray();
int carryOver = 0;
int increment = 1;
for (int i = chars.Length; i > 0; i--)
{
	int ascii = (int)chars[i - 1] - (int)charNull + carryOver + increment;
	carryOver = (ascii - 1) / numberBase;
	ascii = (ascii - 1) % numberBase + 1;
	increment = 0;
	if (ascii > 0)
	{
		output = ((char)((int)charNull + ascii)).ToString() + output;
	}
}
Console.WriteLine(input.ToUpper());
Console.WriteLine(output);

Open in new window

/gustav
0
 
anarki_jimbelCommented:
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

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

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

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

/gustav
0
 
anarki_jimbelCommented:
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?
0
 
Gustav BrockCIOCommented:
Yes, a new letter, A, is only prefixed (as a carryover) when a leading Z will be raised by 1.

/gustav
0
 
anarki_jimbelCommented:
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

0
 
ArkCommented:
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

0
 
ArkCommented:
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

0
 
ArkCommented:
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

0
 
ArkCommented:
Bonus :) Convert integers To/From any system:
Private Function convertBase(input As Integer, radix As Integer, useDigits As Boolean) As String
    Dim result = ""
    Dim Asc_A = Convert.ToInt32("A"c)
    Do While input > If(useDigits, 0, -1)
        Dim x = input Mod radix
        If useDigits AndAlso x < 10 Then
            result = x.ToString & result
        Else
            result = Convert.ToChar(Asc_A + If(useDigits, x - 10, x)) & result
        End If
        input = Math.Floor(input / radix)
        If Not useDigits Then input -= 1
    Loop
    Return result
End Function

Private Function parseBase(input As String, radix As Integer, useDigits As Boolean) As Integer
    input = input.ToUpper
    Dim Asc_A = Convert.ToInt32("A"c)
    '====Validating====
    Dim pattern As String
    If useDigits Then
        pattern = String.Format("[0-{0}", Math.Min(9, radix))
        If radix > 10 Then pattern &= ",A-" & Convert.ToChar(Asc_A + radix - 9)
        pattern &= "]"
    Else
        pattern = String.Format("[A-{0}]", Convert.ToChar(Asc_A + radix - 1))
    End If
    If Not System.Text.RegularExpressions.Regex.IsMatch(input, pattern) Then
        Throw New ArgumentException("Only " & pattern & " chars allowed.", "input")
        Return -1
    End If
    '=================
    Dim length = input.Length, result As Integer
    For Each c As Char In input
        Dim x As Integer
        If useDigits AndAlso Char.IsDigit(c) Then
            x = Integer.Parse(c.ToString)
        Else
            x = Convert.ToInt32(c) - Asc_A + If(useDigits, 10, 1)
        End If
        result += x * Math.Pow(radix, length - 1)
        length -= 1
    Next
    If Not useDigits Then result -= 1
    Return result
End Function

Open in new window

Using:
Private Function ToHex(input As Integer) As String
    Return convertBase(input, 16, True)
End Function
Private Function FromHex(input As String) As Integer
    Return parseBase(input, 16, True)
End Function

Private Function ToOct(input As Integer) As String
    Return convertBase(input, 8, True)
End Function
Private Function FromOct(input As String) As Integer
    Return parseBase(input, 8, True)
End Function

Private Function ToBinary(input As Integer) As String
    Return convertBase(input, 2, True)
End Function
Private Function FromBinary(input As String) As Integer
    Return parseBase(input, 2, True)
End Function

Open in new window

0
 
jxbmaSoftware ConsultantAuthor Commented:
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.
0

Featured Post

Important Lessons on Recovering from Petya

In their most recent webinar, Skyport Systems explores ways to isolate and protect critical databases to keep the core of your company safe from harm.

  • 4
  • 4
  • 4
  • +5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now