Monday, November 16, 2009

c# LinQ SelectMany

I was brushing up on my Linq and reading up on "SelectMany", and created an example that I thought I'd share with you. This uses all the 3.5 new features: Lambda expressions, LINQ, implicitly typed arrays and anon types - declared, instantiated and populated on the fly! It can make for an amazingly compact code!

static void Main(string[] args)
{
    // declare products array which includes categories for each product
    var products = new[] {
            new {name="salon shampoo", price=15.99d, categories = new[]{
                                                 new {name="bath"},
                                                 new {name="luxury"}
                                                }
                },
            new {name="soap", price=1.33d, categories = new[]{
                                                 new {name="bath"}
                                                }
                },
            new {name="asiago bread", price=5.99d, categories = new[]{
                                                 new {name="luxury"},
                                                 new {name="grocery"}
                                                }
                },
            new {name="sugar 1kg", price=1.99d, categories = new[]{
                                                 new {name="grocery"}
                                                }
                }
        };

    // linq query to extract products less than $10 and of category "grocery"
    var groceries = from p in products
                 where p.price < 10D && p.categories.Any(c => c.name == "grocery")
                 select p;
    // Alternatively: "Fluent" syntax is more condensed:
    // var groceries = products.Where(p => p.price < 10D && 
       p.categories.Any(c => c.name == "grocery"));

    //now iterate the result to display the selected product    foreach (var product in result)
    {
        Console.Write(string.Format("\r\nProd:{0}, Price:{1} Categories:", product.name, product.price));
        foreach (var category in product.categories)
        {
            Console.Write(category.name + " ");
        }
    }

    //however SELECTMANY, can show the collection in a "flattened" list, while repeating parent items
    var allProducts = products.SelectMany(p => p.categories, (p, c) =>
            p.name + ", $" + p.price.ToString() + ", cat=" + c.name);
    Console.WriteLine("All Products:" + string.Join("\r\n", allProducts.ToArray()));
    var grocFlattened = groceries.SelectMany(p=>
      p.categories,(p,c)=>p.name + ", $" + p.price.ToString() + ", cat=" + c.name);
    Console.WriteLine("Groceries:" + string.Join("\r\n", grocFlattened.ToArray()));
  //Groceries:
  // asiago bread, $5.99, cat=luxury
  // asiago bread, $5.99, cat=grocery
  // sugar 1kg, $1.99, cat=grocery
}

SelectMany is very powerful and cool as it can "flatten" a collection, but otherwise works just like a Select statement.