7

I have read many sites/threads on select and select many in LINQ but still don't quite understand.

Does select return one element in a collection and select many flatten a collection (eg List>())?

Thanks

GurdeepS
  • 65,107
  • 109
  • 251
  • 387
  • 3
    dupe question - see http://stackoverflow.com/questions/958949/difference-between-select-and-selectmany for a great answer. – Scott Ivey Mar 11 '10 at 14:20
  • Possible duplicate of [Difference Between Select and SelectMany](https://stackoverflow.com/questions/958949/difference-between-select-and-selectmany) – c4ffrey Jul 30 '19 at 11:22

3 Answers3

5

Here's a sample. Hope it clarifies everything:

static void MethodRun()
{
    List<Topping> testToppings = new List<Topping> { Topping.Cheese, Topping.Pepperoni, Topping.Sausage };

    var firstLetterofToppings = testToppings.Select(top => top.ToString().First());

    // returns "C, P, S"

    var singleToppingPizzas = testToppings.Select(top => new Pizza(top)).ToArray();

    // returns "Pizza(Cheese), Pizza(Pepperoni), Pizza(Sausage)"

    List<Topping> firstPizza = new List<Topping> { Topping.Cheese, Topping.Anchovies };
    List<Topping> secondPizza = new List<Topping> { Topping.Sausage, Topping.CanadianBacon, Topping.Pepperoni };
    List<Topping> thirdPizza = new List<Topping> { Topping.Ham, Topping.Pepperoni };

    List<IEnumerable<Topping>> toppingsPurchaseOrder = new List<IEnumerable<Topping>> { firstPizza, secondPizza, thirdPizza };

    var toppingsToOrder = toppingsPurchaseOrder.SelectMany(order => order);

    //returns "Cheese, Anchovies, Sausage, CanadianBacon, Pepperoni, Ham, Pepperoni"

}

class Pizza
{
    public List<Topping> Toppings { get; private set; }
    public Pizza(Topping topping) : this(new List<Topping> { topping }) { }
    public Pizza(IEnumerable<Topping> toppings)
    {
        this.Toppings = new List<Topping>();
        this.Toppings.AddRange(toppings);
    }
}

enum Topping
{
    Cheese,
    Pepperoni,
    Anchovies,
    Sausage,
    Ham,
    CanadianBacon
}

The key is that Select() can select any type of object. It's true that you can select a property of whatever generic value is assigned to your collection, but you can select any other type of object also. SelectMany() just flattens your list.

sidney.andrews
  • 5,146
  • 3
  • 23
  • 29
3

SelectMany returns a number of objects for each of the object passed into the expression. Select istead returns a single object for each object passed into the expression.

To quote the documentation:

Select Many

Projects each element of a sequence to an IEnumerable<(Of <(T>)>) and flattens the resulting sequences into one sequence.

Select

Projects each element of a sequence into a new form.

You can use SelectMany if you want to flatten a hierarchy. E.g. if you have Orders and OrderDetails. If you want make a selection based upon the orders, but you want as a return the OrderDetails use SelectMany.

var result = db.Orders
               .Where(x => x.CustomerId == 500)  // input to next expression is IEnumerable<Order>
               .SelectMany(x => x.OrderDetails)  // input to next expression is IEnumerable<OrderDetails>
               .Sum(x => x.PositionTotal);

var result = db.Orders
               .Where(x => x.CustomerId == 500)  // input to next expression is IEnumerable<Order>
               .Select(x => CustomerName);
AxelEckenberger
  • 16,628
  • 3
  • 48
  • 70
0

Let's go sequentially over your question:

1. Does select return one element in a collection? -> No, absolutely not. 'select' returns exactly the same number of elements that are in the collection, but into different shape(if wanted).

But, yes, it returns one sequence(/collection) having all those elements(e.g. even_sqare in below example).

e.g
int[] even = {2,4}; int[] even_square = even.Select(n=> n*2).ToArray();

o/p
even_square outputs to {4,8} which is exact same in counts (2), but different projection, we gave squares of each by selecting them.

2. & does select many flatten a collection (e.g. List>())?

-> yes, but actually, it's like a cross join with our control.

 int[] odd = { 1, 3 };
 int[] even = { 2, 4 };

 int[] crossjoin = 
         even.SelectMany(
                n => odd, //combining even with odd
                (n,o)=>Convert.ToInt32(n.ToString()+o.ToString())//given many selects, decide our projection
              ).ToArray();

 foreach (var item in crossjoin)
 {
    Console.WriteLine(item);
 }

 Output: 21
         23
         41
         43

Now a million dollar thing:

  • Unlike most operators in linq, SelectMany takes two Collections, instead of one.
  • "even" is first collection, and odd is the second collection, which we passed in using lambda like this 'n = > odd'. (this is where people say it flattens)
  • third parameter(in above statement, second parameter) is TResult which gives us output which is cross join and that's beauty of SelectMany if we understood it.
  • Enjoy learning.
Suraj Yadav
  • 81
  • 1
  • 4