6

I've a

List<String> MyList=new List<string>();

I need to fill the list MyList with n values.

if the value of n is 2 then the list MyList will contain

"A","B"

if 10 then

"A","B","C"....."J"

if 30 then

"A"....."Z","AA","AB",AC","AD"

if 1000 then

"A",....."Z","AA","AB"......"AZ","BA","BB"......."BZ"........"YZ","AAA",AAB".....
and so on

I do not know how to do this.

Please help me to do this using any method Using LINQ or LAMBDA Expression

Thorin Oakenshield
  • 14,232
  • 33
  • 106
  • 146
  • Do you need a csv? You can use a stringbuilder and append n values instead of using a List. Or, do you want to convert an existing List of strings to a csv? – pavanred Nov 02 '10 at 07:30
  • @Pavanred: I don't think any CSV is required here - just a list of strings. – Jon Skeet Nov 02 '10 at 07:32
  • http://stackoverflow.com/questions/314466/generating-an-array-of-letters-in-the-alphabet-in-c – jjj Nov 02 '10 at 07:37
  • 4
    Why "AB"..."AZ" and "ABC"...? What's wrong with "AA" and "AAA"? – Guffa Nov 02 '10 at 07:42
  • @Pramodh: You missed "ZA".."ZZ" :) – Guffa Nov 02 '10 at 08:33
  • @Pramodh: take a look at my "edit 2". I found a quite easy solution. – Stefan Steinegger Nov 02 '10 at 13:58
  • Dupes: http://stackoverflow.com/questions/297213/translate-an-index-into-an-excel-column-name http://stackoverflow.com/questions/181596/how-to-convert-a-column-number-eg-127-into-an-excel-column-eg-aa http://stackoverflow.com/questions/2522720/column-no-to-column-letter-in-excel-vsto-using-c http://stackoverflow.com/questions/837155/fastest-function-to-generate-excel-column-letters-in-c etc. – Ken Nov 02 '10 at 14:05
  • 1
    @Ken: these are not 100% duplicates. It is not the same if you turn a number into a string or if you create a continuous sequence of strings. If you look at my answer, it does not depend on turning numbers into strings. – Stefan Steinegger Nov 03 '10 at 10:06
  • I feel that the most elegant answer to this kind of question would be some sort of recursive algorithm – explorer Nov 03 '10 at 18:50

6 Answers6

6

Edit 2:

This is probably the easiest way to implement it. I tested it, it works fine. You could generate a infinite number of strings.

public IEnumerable<string> GenerateStrings()
{
    foreach(string character in Alphabet())
    {
      yield return character;
    }

    foreach (string prefix in GenerateStrings())
    {
      foreach(string suffix in Alphabet())
      {
        yield return prefix + suffix;
      }
    }
}

public IEnumerable<string> Alphabet()
{
    for(int i = 0; i < 26; i++)
    {
      yield return ((char)('A' + i)).ToString();
    }
}

Stuff I wrote before:

You could also write a little recursive function which returns any string by a certain index. This may not be optimal performance wise, because there are some repetitive divisions, but it may be fast enough for your purpose.

It is quite short and easy:

string GetString(int index)
{
  if (index < 26)
  {
    return ((char)('A' + index)).ToString();
  }
  return GetString(index / 26 - 1) + GetString(index % 26);
}

usage (may also be put into another method:

List<string> strings = Enumerable.Range(0, 1000)
  .Select(x => GetString(x))
  .ToList();

This is working code, just wrote a test for it.


Edit: eg, the "full linq way" application of GetString:

public void IEnumerale<string> GenerateStrings()
{
  int index = 0;
  // generate "infinit" number of values ...
  while (true)
  {
     // ignoring index == int.MaxValue
     yield return GetString(index++);
  }
}

List<string> strings = GenerateStrings().Take(1000).ToList();
larsw
  • 3,790
  • 2
  • 25
  • 37
Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • Nice approach, though `int i suffixCounter = 0;` doesn't compile, and you don't use `i` nor `suffixCounter`. – Kobi Nov 03 '10 at 05:09
4

I did something similar in SQL a while back.

Translated to C# this is a function to create a code from a number:

public static string GetCode(int id) {
  string code, chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  if (id <= chars.Length) {
    code = chars.Substring(id - 1, 1);
  } else {
    id--;
    int value = chars.Length, adder = 0;
    while (id >= value * (chars.Length + 1) + adder) {
      adder += value;
      value *= chars.Length;
    }
    code = chars.Substring((id - adder) / value - 1, 1);
    id = ((id - adder) % value);
    while (value > 1) {
      value /= chars.Length;
      code += chars.Substring(id / value, 1);
      id = id % value;
    }
  }
  return code;
}

Then you just get numbers from 1 and up, and translate into codes:

var codes = Enumerable.Range(1, 1000).Select(n => GetCode(n));

The limit of the function is currently "ZZZZZZ" or 321272406. (After that you get a division by zero.)

Note that this function uses all combinations and returns "A".."Z", "AA".."ZZ", "AAA"..."ZZZ" rather than starting at "AB" and "ABC".

Community
  • 1
  • 1
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
3

This is similar to this question (but not quite enough to mark it as a duplicate, and it's a hard problem to search for anyway).

Use any of the working IEnumerable<string> answers (or at least, any which cover the range you need), and then if you need to create a list with a certain number of elements, just use:

List<string> list = GenerateSequence().Take(count).ToList();
Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    It's not really an answer to the question imo. – Stefan Steinegger Nov 02 '10 at 13:59
  • It's an easy problem to search for if you recognize it's what Excel does -- try searching for "excel column char" or the like. :-) – Ken Nov 02 '10 at 14:06
  • 4
    @Stefan: In what way is it not an answer to the question? Take an answer from the other question, add the extra line, and you're done. What's wrong with it, precisely? – Jon Skeet Nov 02 '10 at 14:10
  • @Jon: The question is about generating strings in a certain way. You answer is more about the usage of Take when you already solved the problem. – Stefan Steinegger Nov 03 '10 at 05:46
  • 1
    Stefan: I disagree. If you follow Jon's link, you'll see several answers to the question about how to generate the strings in a certain way. The only missing part is the `.Take().ToList()` that Jon added here. – Gabe Nov 03 '10 at 05:54
  • @Gabe/Jon. Ok sorry, I didn't read the answers on the linked question before. The answers there look indeed very similar. – Stefan Steinegger Nov 03 '10 at 06:14
3

This code is working fine, but I'm not sure if it's "LINQ enough".

char[] validChars = Enumerable.Range(0, 26).Select(i => (char)('A' + i)).ToArray();
List<string> result = new List<string>();
List<string> generator = validChars.Select(ch => ch.ToString()).ToList();

int n = 1000;

while (result.Count < n)
{
    result.AddRange(generator);
    generator = generator.Take((n - result.Count) / validChars.Length + 1)
                         .SelectMany(s => validChars.Select(ch => s + ch))
                         .ToList();
}

var output = result.Take(n);
Cheng Chen
  • 42,509
  • 16
  • 113
  • 174
  • It skips from "AZ" directly to "ABC", and from "ABZ" to "ABCD". – Guffa Nov 02 '10 at 08:38
  • 2
    @Danny Chen: Now it works, but it's not very efficient... It creates half a million strings to get those first thousand... – Guffa Nov 02 '10 at 09:45
  • @Guffa: I have to agree with you :( this problem is brought by `SelectMany`, which generates continuous chars in batch. But I think if n is not so large, it's a good idea because the code is more readable and easy to manage. – Cheng Chen Nov 02 '10 at 09:53
  • @Guffa: I add a `Take` method before `SelectMany`, now it's more effcient. Thanks for your advice. – Cheng Chen Nov 02 '10 at 10:05
  • @Danny Chen: Now you always create a collection that is never used before exiting the loop. How about starting with `generator = new List { "" }` and populate generator first in the loop. :) – Guffa Nov 02 '10 at 17:20
  • @Guffa: You genius! Hit the nail on the head :) But if I start with `generator` empty, how can I generate the list to `AddRange` with `SelectMany`? It seems that the logic in the first loop is different with next loops. – Cheng Chen Nov 03 '10 at 03:16
  • @Danny Chen: "" + ch = ch, so { "" }.SelectMany(...) = validChars. :) – Guffa Nov 03 '10 at 06:11
1

Try the below .. I am using Cross Join and a Union to build the source and then filtering the record by using the Take extension method

char[] charArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();

List<String> MyList = new List<string>();
int n = 1000;

                      (from value1 in charArray
                       select new
                       {
                           newString = value1.ToString()
                       })
                   .Union
                   (
                       (from value1 in charArray
                        from value2 in charArray

                        select new
                        {
                            newString = string.Concat(value1, value2)
                        })
                    )
                    .Union
                    (
                        (from value1 in charArray
                         from value2 in charArray
                         from value3 in charArray

                         select new
                         {
                             newString = string.Concat(value1, value2, value3)
                         })
                     )
                     .Take(n)
                     .ToList()                         
                     .ForEach(i => MyList.Add(i.newString));

Hope this will give you some idea of using a combination of Linq,Lambda and Extension method.

priyanka.sarkar
  • 25,766
  • 43
  • 127
  • 173
0

@DannyChen this is it. your code with a little changes..

char[] validChars = Enumerable.Range(0, 26).Select(i => (char)('A' + i)).ToArray();

int n = 30;
int pointer = 0;
int pointerSec = 0;
int Deg = 0;
string prefix = string.Empty;
string prefixMore = string.Empty;
List<string> result = new List<string>();

while (n > 0)
{
    result.AddRange(validChars.Skip(pointer).Select(ch => prefix + ch).Take(n));
    if (pointer == 26)
    { 
        pointer = -1;
        Deg += 1;
        prefixMore = "" + validChars[pointerSec];
        pointerSec++;
        n++;
    }
    else
    {
        if (Deg == 0)
        {
            prefix = "" + validChars[pointer];
        }
        else
        {
            prefix = prefixMore + validChars[pointer];
        }
    }
    n--;
    pointer++;
}

it is 100% correct.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
jjj
  • 605
  • 1
  • 9
  • 26