views:

2634

answers:

8

Hi i have question about iterate throught 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 an char "a" to the function then the function must return b. if u give "bnc" then the function must return "bnd"

A: 

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
+2  A: 

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
I love the way our answers are so similar - just differing in whether they build a list or yield lazily :)
Jon Skeet
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
+10  A: 

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
damn i had the same idea but too late :)
Idan K
thanks a lot :)
subprime
Downvoters: care to explain why?
Jon Skeet
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
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
The solution by TheSoftwareJedi is way better and VERY clean. That is the generic pattern to use. Probably why you have been downvoted.
Wolf5
@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
@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
(It also hard-codes the "alphabet" in use... that could be fixed reasonably easily though.)
Jon Skeet
Ooh... just thought of how to make my solution somewhat cleaner. Editing...
Jon Skeet
@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
@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
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
Just keep the IEnumerator<string> and call MoveNext manually.
Jon Skeet
@subprime see my latest edit
TheSoftwareJedi
A: 

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
This skips from az to aaa
TheSoftwareJedi
+9  A: 

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
This skips from z to ba.
Jon Skeet
Thanks, fixed. Cheers!
TheSoftwareJedi
The best solution as is the one that ought to be marked as the answer.
Wolf5
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
@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
sorry TheSoftwareJedi for my description but this is the answer :) Thanks to all others!
subprime
@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
@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
A: 

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
programm only from a to zz. no answer sorry :(
subprime
Ah ok, you'd updated your question after...nevermind
Charlie
yes sorry :) my fault
subprime
A: 

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
A: 

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