18

I have a question about iterate through the Alphabet. I would like to have a loop that begins with "a" and ends with "z". After that, the loop begins "aa" and count to "az". after that begins with "ba" up to "bz" and so on...

Anybody know some solution?

Thanks

EDIT: I forgot that I give a char "a" to the function then the function must return b. if u give "bnc" then the function must return "bnd"

Kindle Q
  • 944
  • 2
  • 19
  • 28
subprime
  • 1,217
  • 8
  • 20
  • 34

10 Answers10

31

First effort, with just a-z then aa-zz

public static IEnumerable<string> GetExcelColumns()
{
    for (char c = 'a'; c <= 'z'; c++)
    {
        yield return c.ToString();
    }
    char[] chars = new char[2];
    for (char high = 'a'; high <= 'z'; high++)
    {
        chars[0] = high;
        for (char low = 'a'; low <= 'z'; low++)
        {
            chars[1] = low;
            yield return new string(chars);
        }
    }
}

Note that this will stop at 'zz'. Of course, there's some ugly duplication here in terms of the loops. Fortunately, that's easy to fix - and it can be even more flexible, too:

Second attempt: more flexible alphabet

private const string Alphabet = "abcdefghijklmnopqrstuvwxyz";

public static IEnumerable<string> GetExcelColumns()
{
    return GetExcelColumns(Alphabet);
}

public static IEnumerable<string> GetExcelColumns(string alphabet)
{
    foreach(char c in alphabet)
    {
        yield return c.ToString();
    }
    char[] chars = new char[2];
    foreach(char high in alphabet)
    {
        chars[0] = high;
        foreach(char low in alphabet)
        {
            chars[1] = low;
            yield return new string(chars);
        }
    }
}

Now if you want to generate just a, b, c, d, aa, ab, ac, ad, ba, ... you'd call GetExcelColumns("abcd").

Third attempt (revised further) - infinite sequence

public static IEnumerable<string> GetExcelColumns(string alphabet)
{
    int length = 0;
    char[] chars = null;
    int[] indexes = null;
    while (true)
    {
        int position = length-1;
        // Try to increment the least significant
        // value.
        while (position >= 0)
        {
            indexes[position]++;
            if (indexes[position] == alphabet.Length)
            {
                for (int i=position; i < length; i++)
                {
                    indexes[i] = 0;
                    chars[i] = alphabet[0];
                }
                position--;
            }
            else
            {
                chars[position] = alphabet[indexes[position]];
                break;
            }
        }
        // If we got all the way to the start of the array,
        // we need an extra value
        if (position == -1)
        {
            length++; 
            chars = new char[length];
            indexes = new int[length];
            for (int i=0; i < length; i++)
            {
                chars[i] = alphabet[0];
            }
        }
        yield return new string(chars);
    }
}

It's possible that it would be cleaner code using recursion, but it wouldn't be as efficient.

Note that if you want to stop at a certain point, you can just use LINQ:

var query = GetExcelColumns().TakeWhile(x => x != "zzz");

"Restarting" the iterator

To restart the iterator from a given point, you could indeed use SkipWhile as suggested by thesoftwarejedi. That's fairly inefficient, of course. If you're able to keep any state between call, you can just keep the iterator (for either solution):

using (IEnumerator<string> iterator = GetExcelColumns())
{
    iterator.MoveNext();
    string firstAttempt = iterator.Current;

    if (someCondition)
    {
        iterator.MoveNext();
        string secondAttempt = iterator.Current;
        // etc
    }
}

Alternatively, you may well be able to structure your code to use a foreach anyway, just breaking out on the first value you can actually use.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • damn i had the same idea but too late :) – Idan K Jun 18 '09 at 09:47
  • okay thanks it works! i must reprogramme to examines the next character and if none is specified then it should begin with a. thanks a lot :) – subprime Jun 18 '09 at 10:28
  • NB. This does assume ASCII (or a superset of ASCII, which includes Unicode). This approach will not work in EBCDIC and other such encodings (letters are not contiguous). However as .NET is Unicode internally this specific implementation is OK. – Richard Jun 18 '09 at 10:52
  • 2
    The solution by TheSoftwareJedi is way better and VERY clean. That is the generic pattern to use. Probably why you have been downvoted. – Wolf5 Jun 18 '09 at 11:02
  • @Richard: In what way does it assume that? You give it whatever alphabet you want. You can give it "agx" if you want and it will yield "a", "g", "x", "aa", "ag", "ax", "ga" etc. – Jon Skeet Jun 18 '09 at 11:11
  • @Wolf5: I agree to some extent. On the other hand, my solution can yield results *forever* - it's not bound by an Int64. It's also rather more efficient at creating strings :) – Jon Skeet Jun 18 '09 at 11:12
  • (It also hard-codes the "alphabet" in use... that could be fixed reasonably easily though.) – Jon Skeet Jun 18 '09 at 11:14
  • @Jon: it is possible to find only the next. sample: i have "agh" and the next is "agi" ? and if nothing at prefix then return a ? – subprime Jun 18 '09 at 11:35
  • @unknown: That should be possible, but it would be a slightly different solution. Basically you'd have to translate the current value ("agh") into the constituent indexes (0, 6, 7) and then update those indexes appropriately. Somewhat messy. I can look at it if you want, but it won't be very nice. – Jon Skeet Jun 18 '09 at 11:41
  • problem i have is that i create a pattern. Part One of pattern get a character "a" but if the part with features dont exists i must create a new character "b". To hold that dynamically i need this function. U understand what i mean ? – subprime Jun 18 '09 at 11:51
  • Just keep the IEnumerator and call MoveNext manually. – Jon Skeet Jun 18 '09 at 12:30
27

Edit: Made it do exactly as the OP's latest edit wants

This is the simplest solution, and tested:

static void Main(string[] args)
{
    Console.WriteLine(GetNextBase26("a"));
    Console.WriteLine(GetNextBase26("bnc"));
}

private static string GetNextBase26(string a)
{
    return Base26Sequence().SkipWhile(x => x != a).Skip(1).First();
}

private static IEnumerable<string> Base26Sequence()
{
    long i = 0L;
    while (true)
        yield return Base26Encode(i++);
}

private static char[] base26Chars = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
private static string Base26Encode(Int64 value)
{
    string returnValue = null;
    do
    {
        returnValue = base26Chars[value % 26] + returnValue;
        value /= 26;
    } while (value-- != 0);
    return returnValue;
}
TheSoftwareJedi
  • 34,421
  • 21
  • 109
  • 151
  • The best solution as is the one that ought to be marked as the answer. – Wolf5 Jun 18 '09 at 11:02
  • Now that it works there's certainly no reason to downvote it. (I didn't downvote even when it was a little bit broken.) Personally I'd ditch the "until" part - separate the concerns of building an infinite sequence from the concern of truncating it - LINQ can do the latter bit fine. But variety is good :) – Jon Skeet Jun 18 '09 at 11:43
  • @Jon I agree. I was just following suit since he seemed to like that. Now his latest edit is starting to get to the real problem. Still not completely there... I think he's trying to find an the first unused key from some Set using this sequence. But I can't tell. – TheSoftwareJedi Jun 18 '09 at 13:23
  • sorry TheSoftwareJedi for my description but this is the answer :) Thanks to all others! – subprime Jun 18 '09 at 13:49
  • @TSJ: Given the fairly simple representation your iterator uses (i.e. a long) it should be fairly easy to convert back from base26 to a long, and then create an iterator which starts at the right place. – Jon Skeet Jun 18 '09 at 13:51
  • @Jon Skeet: Why is it that whenever we both jump on a SO question, a battle royale occurs? Are you just upset that I'm a Jedi? I can meet with the counsel and see if they'll hold a special session to appoint you a position. lol... – TheSoftwareJedi Jun 18 '09 at 15:11
3

The following populates a list with the required strings:

List<string> result = new List<string>();
for (char ch = 'a'; ch <= 'z'; ch++){
    result.Add (ch.ToString());
}

for (char i = 'a'; i <= 'z'; i++)
{
    for (char j = 'a'; j <= 'z'; j++)
    {
        result.Add (i.ToString() + j.ToString());
    }
}
Patrick McDonald
  • 64,141
  • 14
  • 108
  • 120
  • I love the way our answers are so similar - just differing in whether they build a list or yield lazily :) – Jon Skeet Jun 18 '09 at 09:47
  • I come from VB so never usually remember the yield, in this case I did just after posting but by the time I tried it out you had already posted the yield solution :) – Patrick McDonald Jun 18 '09 at 09:50
3

I know there are plenty of answers here, and one's been accepted, but IMO they all make it harder than it needs to be. I think the following is simpler and cleaner:

static string NextColumn(string column){
    char[] c = column.ToCharArray();
    for(int i = c.Length - 1; i >= 0; i--){
        if(char.ToUpper(c[i]++) < 'Z')
            break;
        c[i] -= (char)26;
        if(i == 0)
            return "A" + new string(c);
    }
    return new string(c);
}

Note that this doesn't do any input validation. If you don't trust your callers, you should add an IsNullOrEmpty check at the beginning, and a c[i] >= 'A' && c[i] <= 'Z' || c[i] >= 'a' && c[i] <= 'z' check at the top of the loop. Or just leave it be and let it be GIGO.

You may also find use for these companion functions:

static string GetColumnName(int index){
    StringBuilder txt = new StringBuilder();
    txt.Append((char)('A' + index % 26));
    //txt.Append((char)('A' + --index % 26));
    while((index /= 26) > 0)
        txt.Insert(0, (char)('A' + --index % 26));
    return txt.ToString();
}
static int GetColumnIndex(string name){
    int rtn = 0;
    foreach(char c in name)
        rtn = rtn * 26 + (char.ToUpper(c) - '@');
    return rtn - 1;
    //return rtn;
}

These two functions are zero-based. That is, "A" = 0, "Z" = 25, "AA" = 26, etc. To make them one-based (like Excel's COM interface), remove the line above the commented line in each function, and uncomment those lines.

As with the NextColumn function, these functions don't validate their inputs. Both with give you garbage if that's what they get.

P Daddy
  • 28,912
  • 9
  • 68
  • 92
3

Here’s what I came up with.

/// <summary>
/// Return an incremented alphabtical string
/// </summary>
/// <param name="letter">The string to be incremented</param>
/// <returns>the incremented string</returns>
public static string NextLetter(string letter)
{
  const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  if (!string.IsNullOrEmpty(letter))
  {
    char lastLetterInString = letter[letter.Length - 1];

    // if the last letter in the string is the last letter of the alphabet
    if (alphabet.IndexOf(lastLetterInString) == alphabet.Length - 1) 
    {
        //replace the last letter in the string with the first leter of the alphbat and get the next letter for the rest of the string
        return NextLetter(letter.Substring(0, letter.Length - 1)) + alphabet[0];
    }
    else 
    {
      // replace the last letter in the string with the proceeding letter of the alphabet
      return letter.Remove(letter.Length-1).Insert(letter.Length-1, (alphabet[alphabet.IndexOf(letter[letter.Length-1])+1]).ToString() );
    }
  }
  //return the first letter of the alphabet
  return alphabet[0].ToString();
}
Julian
  • 31
  • 3
1

just curious , why not just

    private string alphRecursive(int c) {
         var alphabet = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
         if (c >= alphabet.Length) {
             return alphRecursive(c/alphabet.Length) + alphabet[c%alphabet.Length];
         } else {
             return "" + alphabet[c%alphabet.Length];
         }
    }
vittore
  • 17,449
  • 6
  • 44
  • 82
  • When c = 26, this returns a. You can modify the if/else to the following: if (c > alphabet.Length - 1) { return alphRecursive(c / alphabet.Length) + alphabet[c % alphabet.Length]; } else { return "" + alphabet[c]; } – Brent Barbata May 30 '13 at 02:41
  • @BrentBarbata yeah it should've been `if (c >= alphabet.Length) {...` – vittore May 30 '13 at 02:48
  • Great solution nonetheless. I just used it to associate alphabetic pins on a google map to table rows counted numerically. Thanks! – Brent Barbata May 30 '13 at 02:52
0

This is like displaying an int, only using base 26 in stead of base 10. Try the following algorithm to find the nth entry of the array

q = n div 26;
r = n mod 26;
s = '';
while (q > 0 || r > 0) {
  s = alphabet[r] + s;
  q = q div 26;
  r = q mod 26;
}

Of course, if you want the first n entries, this is not the most efficient solution. In this case, try something like daniel's solution.

Martijn
  • 5,471
  • 4
  • 37
  • 50
0

I gave this a go and came up with this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Alphabetty
{
    class Program
    {
        const string alphabet = "abcdefghijklmnopqrstuvwxyz";
        static int cursor = 0;
        static int prefixCursor;
        static string prefix = string.Empty;
        static bool done = false;
        static void Main(string[] args)
        {
            string s = string.Empty;
            while (s != "Done")
            {
                s = GetNextString();
                Console.WriteLine(s);
            }
            Console.ReadKey();

        }        
        static string GetNextString()
        {
            if (done) return "Done";
            char? nextLetter = GetNextLetter(ref cursor);
            if (nextLetter == null)
            {
                char? nextPrefixLetter = GetNextLetter(ref prefixCursor);
                if(nextPrefixLetter == null)
                {
                    done = true;
                    return "Done";
                }
                prefix = nextPrefixLetter.Value.ToString();
                nextLetter = GetNextLetter(ref cursor);
            }

            return prefix + nextLetter;
        }

        static char? GetNextLetter(ref int letterCursor)
        {
            if (letterCursor == alphabet.Length)
            {
                letterCursor = 0;
                return null;
            }

            char c = alphabet[letterCursor];
            letterCursor++;
            return c;
        }
    }
}
Charlie
  • 10,227
  • 10
  • 51
  • 92
0

Here is something I had cooked up that may be similar. I was experimenting with iteration counts in order to design a numbering schema that was as small as possible, yet gave me enough uniqueness.

I knew that each time a added an Alpha character, it would increase the possibilities 26x but I wasn't sure how many letters, numbers, or the pattern I wanted to use.

That lead me to the code below. Basically you pass it an AlphaNumber string, and every position that has a Letter, would eventually increment to "z\Z" and every position that had a Number, would eventually increment to "9".

So you can call it 1 of two ways..

//This would give you the next Itteration... (H3reIsaStup4dExamplf)
string myNextValue = IncrementAlphaNumericValue("H3reIsaStup4dExample") 

//Or Loop it resulting eventually as "Z9zzZzzZzzz9zZzzzzzz"
string myNextValue = "H3reIsaStup4dExample"
while (myNextValue != null)
{
   myNextValue = IncrementAlphaNumericValue(myNextValue)
   //And of course do something with this like write it out
}

(For me, I was doing something like "1AA000")

public string IncrementAlphaNumericValue(string Value)
    {
        //We only allow Characters a-b, A-Z, 0-9
        if (System.Text.RegularExpressions.Regex.IsMatch(Value, "^[a-zA-Z0-9]+$") == false)
        {
            throw new Exception("Invalid Character: Must be a-Z or 0-9");
        }

        //We work with each Character so it's best to convert the string to a char array for incrementing
        char[] myCharacterArray = Value.ToCharArray();

        //So what we do here is step backwards through the Characters and increment the first one we can. 
        for (Int32 myCharIndex = myCharacterArray.Length - 1; myCharIndex >= 0; myCharIndex--)
        {
            //Converts the Character to it's ASCII value
            Int32 myCharValue = Convert.ToInt32(myCharacterArray[myCharIndex]);

            //We only Increment this Character Position, if it is not already at it's Max value (Z = 90, z = 122, 57 = 9)
            if (myCharValue != 57 && myCharValue != 90 && myCharValue != 122)
            {
                myCharacterArray[myCharIndex]++;

                //Now that we have Incremented the Character, we "reset" all the values to the right of it
                for (Int32 myResetIndex = myCharIndex + 1; myResetIndex < myCharacterArray.Length; myResetIndex++)
                {
                    myCharValue = Convert.ToInt32(myCharacterArray[myResetIndex]);
                    if (myCharValue >= 65 && myCharValue <= 90)
                    {
                        myCharacterArray[myResetIndex] = 'A';
                    }
                    else if (myCharValue >= 97 && myCharValue <= 122)
                    {
                        myCharacterArray[myResetIndex] = 'a';
                    }
                    else if (myCharValue >= 48 && myCharValue <= 57)
                    {
                        myCharacterArray[myResetIndex] = '0';
                    }
                }

                //Now we just return an new Value
                return new string(myCharacterArray);
            } 
        }

        //If we got through the Character Loop and were not able to increment anything, we retun a NULL. 
        return null;  
    }
da_jokker
  • 954
  • 2
  • 12
  • 16
-2

Here's my attempt using recursion:

public static void PrintAlphabet(string alphabet, string prefix)
{
    for (int i = 0; i < alphabet.Length; i++) {
        Console.WriteLine(prefix + alphabet[i].ToString());
    }

    if (prefix.Length < alphabet.Length - 1) {
        for (int i = 0; i < alphabet.Length; i++) {
            PrintAlphabet(alphabet, prefix + alphabet[i]);
        }
    }
}

Then simply call PrintAlphabet("abcd", "");

dariom
  • 4,413
  • 28
  • 42