24

I want to generate a list in C#. I am missing python's list comprehensions. Is there a C# way to create collections on the fly like list comprehensions or generator expressions do in python?

Sнаđошƒаӽ
  • 16,753
  • 12
  • 73
  • 90
minty
  • 22,235
  • 40
  • 89
  • 106

5 Answers5

29

If you are using C# 3.0 (VS2008) then LINQ to Objects can do very similar things:

List<Foo> fooList = new List<Foo>();
IEnumerable<Foo> extract = from foo in fooList where foo.Bar > 10 select Foo.Name.ToUpper();
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
Matt Campbell
  • 1,152
  • 9
  • 15
15

Matt has mentioned query expressions. These are available for LINQ in general, by the way - not just LINQ to Objects. (For example, the same query applied to a LINQ to SQL datacontext would execute the filter and projection on the database.)

The query expressions in C# 3 are simply syntactic sugar over writing normal C# code - although query expressions usually end up calling extension methods. (They don't have to, and the compiler doesn't care, but they usually do.) There are various things you can do with collections which aren't available in C# query expressions, but which are supported by method calls, so it's worth being aware of both kinds of syntax. For instance, Matt's query expression of:

List<Foo> fooList = new List<Foo>();
IEnumerable<string> extract = from foo in fooList where foo.Bar > 10 select foo.Name.ToUpper();

is "pre-processed" into:

List<Foo> fooList = new List<Foo>();
IEnumerable<string> extract = fooList.Where(foo => foo.Bar > 10)
                                     .Select(foo => foo.Name.ToUpper());

If you want to (say) filter based on the index of the value in the original collection, you can use an appropriate overload of Where which is unavailable via query expressions:

List<Foo> fooList = new List<Foo>();
IEnumerable<string> extract = fooList.Where((foo, index) => foo.Bar > 10 + index)
                                     .Select(foo => foo.Name.ToUpper());

Or you could find the length of the longest name matching the criteria:

List<Foo> fooList = new List<Foo>();
int longestName = fooList.Where((foo, index) => foo.Bar > 10 + index)
                         .Select(foo => foo.Name)
                         .Max();

(You don't have to do the projection and max in separate methods - there's a Max overload which takes a projection as well.)

My point is that using extension methods you can very easily build up sophisticated queries.

You mention Python generators as well - C# has this in the form of iterator blocks. Indeed, these are incredibly useful when implementing LINQ-like operators. (Because most of LINQ to Objects is based on extension methods, you can add your own operators which look "native" to LINQ - although you can't change the query expression syntax yourself.)

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Python's list comprehension can create a list from scratch in addition to *extracting* from another list. As far as I understand, what you have shown *extracts* from an existing list. But the OP mentions *generate* and *create* in their post. I think my answer does the creating thing. Please correct me if I am wrong. After all, you only looked at the question... and the question [answered itself](http://meta.stackexchange.com/a/9147/282546) ;-) – Sнаđошƒаӽ Feb 23 '17 at 10:48
  • 1
    @Sнаđошƒаӽ: The question isn't really clear enough, to be honest - it would have been closed if it had been raised these days. I'm not going to put any more effort into an answer on an unclear question... although I'd personally use `Enumerable.Range` to act as a "source" rather than the `for` loop in your answer. – Jon Skeet Feb 23 '17 at 11:15
  • Thanks for `Enumerable.Range` pointer. Other than that, is that C# code equivalent to the python's list comprehension? Or, is there any more elegant way in C#? Compared to python's one liner, that C# code looks bulky. – Sнаđошƒаӽ Feb 27 '17 at 13:46
  • 1
    @Sнаđошƒаӽ: Well it would be `string.Join(", ", Enumerable.Range(1, 50).Select(x => $"cb{x:00}")))` which really isn't that long... – Jon Skeet Feb 27 '17 at 14:15
  • That `$` thing right there is C# 6 thing, if I am not mistaken, which has really made it a lot simpler and elegant. Thanks. – Sнаđошƒаӽ Feb 27 '17 at 14:54
  • 1
    @Sнаđошƒаӽ: Well, you'd only need to change `$"cb{x:00}"` to `string.Format("cb{0:00}", x)`, so there's not *very* much difference - but yes, it's still an improvement. – Jon Skeet Feb 27 '17 at 15:38
7

List<T>.ConvertAll behaves just like list comprehensions by performing the same operation on every item on an existing list and then returning a new collection. This is an alternative to using Linq especially if you are still using .NET 2.0.

In Python, a simple list comprehension example:

>>> foo = [1, 2, 3]
>>> bar = [x * 2 for x in foo]
>>> bar
[2, 4, 6]

For C# 3.0, you can pass a lambda function specifying what type of mapping function is needed.

public static void Main()
{
    var foo = new List<int>{ 1, 2, 3};
    var bar = foo.ConvertAll(x => x * 2);    // list comprehension

    foreach (var x in bar)
    {
        Console.WriteLine(x);  // should print 2 4 6
    }
}

For C# 2.0, you can use an anonymous method with the Converter delegate to perform the equivalent.

public static void Main()
{
    List<int> foo = new List<int>(new int[]{ 1, 2, 3});
    List<int> bar = foo.ConvertAll(new Converter<int, int>(delegate(int x){ return x * 2; }));  // list comprehension

    foreach (int x in bar)
    {
        Console.WriteLine(x);  // should print 2 4 6
    }
}

(Note: the same can be done with Arrays using Array.ConvertAll

Ray
  • 187,153
  • 97
  • 222
  • 204
3

I know this is very very late an answer, but I too wondered if C# has anything equivalent to python's list comprehension. Answers from both Matt Campbell and Jon Skeet show how to extract a list from an existing one, as far as I understood. But a python's list comprehension can also create a list from scratch. So here is what I came up with.

First I will show the python list comprehension, and then its C# equivalent that I came up with.

The toy task is to create a string like this

cb01, cb02, cb02, ... , cb50

Python list comprehension:

s = ', '.join('cb{0:02}'.format(i+1) for i in range(50))

C# equivalent I came up with:

string s = String.Join(", ", new Func<List<string>>( () =>
{
    List<string> list = new List<string>();
    foreach (int i in Enumerable.Range(1, 50))
        list.Add(String.Format("cb{0:00}", i));

    return list;
}).Invoke());

I am not sure if I over-did anything or not though.


Edit: (from what Jon Skeet mentioned in his comment, a real one-liner equivalent to python's list comprehension)

String.Join(", ", Enumerable.Range(1, 50).Select(x => $"cb{x:00}")))

Note that $ thing is a C# 6 feature. If you are still not using C# 6, you can go with the old String.Format() way.

Sнаđошƒаӽ
  • 16,753
  • 12
  • 73
  • 90
1

There's this:

new List<FooBar> { new Foo(), new Bar() }

which is only a little longer than its python equivalent:

[Foo(), Bar()]

And then there is this:

public IEnumerable<FooBar> myFooBarGenerator() {
     yield return new Foo();
     yield return new Bar();
}

which is the python equivalent of:

def myFooBarGenerator():
    yield Foo()
    yield Bar()
Sнаđошƒаӽ
  • 16,753
  • 12
  • 73
  • 90
Nachbars Lumpi
  • 2,688
  • 1
  • 21
  • 13