Solved

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

Posted on 2016-10-25
17
75 Views
Last Modified: 2016-10-31
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
Comment
Question by:jxbma
  • 4
  • 4
  • 4
  • +5
17 Comments
 
LVL 49

Expert Comment

by:Ryan Chong
Comment Utility
just a quick guess... you probably need to check the ascii of the input char, then you need that ascii + 1
0
 
LVL 10

Expert Comment

by:Duy Pham
Comment Utility
I would try to map A, B, ..., Z to 0, 1, ..., 25, and then use base 26 to get the next (output) value.
0
 
LVL 23

Expert Comment

by:Dr. Klahn
Comment Utility
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
 
LVL 21

Expert Comment

by:Kim Walker
Comment Utility
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
 
LVL 49

Accepted Solution

by:
Gustav Brock earned 250 total points
Comment Utility
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
 
LVL 29

Expert Comment

by:anarki_jimbel
Comment Utility
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
 
LVL 49

Expert Comment

by:Gustav Brock
Comment Utility
No, it fails for a string like ZZZ which should output as AAAA but becomes AAA only.

/gustav
0
 
LVL 29

Expert Comment

by:anarki_jimbel
Comment Utility
Wow, didn't notice the requirement. A bit strange one...

OK, just forget. Didn't read carefully ... :)
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 49

Expert Comment

by:Gustav Brock
Comment Utility
Well, basically this is just a base 26 number system with members represented by letters from A to Z.

/gustav
0
 
LVL 29

Expert Comment

by:anarki_jimbel
Comment Utility
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
 
LVL 49

Expert Comment

by:Gustav Brock
Comment Utility
Yes, a new letter, A, is only prefixed (as a carryover) when a leading Z will be raised by 1.

/gustav
0
 
LVL 29

Expert Comment

by:anarki_jimbel
Comment Utility
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
 
LVL 27

Expert Comment

by:Ark
Comment Utility
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
 
LVL 27

Expert Comment

by:Ark
Comment Utility
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
 
LVL 27

Expert Comment

by:Ark
Comment Utility
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
 
LVL 27

Assisted Solution

by:Ark
Ark earned 250 total points
Comment Utility
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
 
LVL 1

Author Closing Comment

by:jxbma
Comment Utility
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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

In this article you'll learn how to use Ajax calls within your CodeIgniter application. To explain this, I'll illustrate how to implement a simple contact form to allow visitors to send you an email through your web site.
Boost your ability to deliver ambitious and competitive web apps by choosing the right JavaScript framework to best suit your project’s needs.
The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn the basics of jQuery, including how to invoke it on a web page. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery.: (CODE)

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now