Linq Query to IEnumerable<T> Extension Method

16,110

Solution 1

That one's easy:

var query = items.Select(i => i.ID);

A select clause always corresponds to a call to Select. Some of the other operators end up with a rather more complex expansion :) If you work hard, you can get the compiler to do some very odd stuff...

You can find all the details of this and other query expression translations in section 7.16 of the C# specification (v3 or v4).

<plug> You could also buy C# in Depth, 2nd edition and read chapter 11 if you really wanted to :)</plug>

Solution 2

You can use this:

var query = items.Select(i => i.ID);

A couple of other points:

Here you don't need the call to ToArray:

foreach (string s in query.ToArray())

Also if your list is large and you are removing a lot of items you may want to use List.RemoveAll instead of iterating. Every time you remove an item from a list all the other items after it have to be moved to fill the gap. If you use RemoveAll this only has to be done once at the end, instead of once for every removed item.

List<Item> itemsToRemove = (List<Item>)ViewState["ItemsToRemove"];
HashSet<string> itemIds = new HashSet<string>(itemsToRemove.Select(s => s.ID));
saleItems.RemoveAll(c => itemIds.Contains(c.ID));

Solution 3

public static class ItemCollectionExtensions
{
    public static IEnumerable<int> GetItemIds(this List<Item> list)
    {
        return list.Select(i => i.ID);
    }
}
Share:
16,110
CSharpNoob
Author by

CSharpNoob

Updated on June 29, 2022

Comments

  • CSharpNoob
    CSharpNoob almost 2 years

    Consider this,

         class Item
         { 
            public string ID { get; set;}
            public string Description { get; set; }
         }
    
         class SaleItem
         { 
            public string ID { get; set;}
            public string Discount { get; set; }
         }
    
    
         var itemsToRemoved = (List<Item>)ViewState["ItemsToRemove"];
         // get only rows of ID
         var query = from i in itemsToRemoved select i.ID;
    
         var saleItems= (List<SaleItem>)ViewState["SaleItems"];
         foreach (string s in query.ToArray())
         {
                saleItems.RemoveItem(s);
         }
    

    How can I write this LINQ phrase using IEnumerable/List Extension methods

      // get only rows of ID
       var query = from i in items select i.ID;
    

    thanks in advance.

  • CSharpNoob
    CSharpNoob over 13 years
    thanks Jon. i have another question, which is faster, the enumerator's extension methods or LInQ queries?
  • Jon Skeet
    Jon Skeet over 13 years
    @CsharpNoob: The difference only exists in source code. The compiled form is exactly the same.
  • CSharpNoob
    CSharpNoob over 13 years
    i though the RemoveAll extension method evaluates only a single argument.in my example the ID is per Item so i need to iterate each item because they have different IDs
  • Mark Byers
    Mark Byers over 13 years
    @CSharpNoob: Could you use hashSet.Contains as your predicate?
  • Mark Byers
    Mark Byers over 13 years
    @CSharpNoob: What is List.RemoveItem anyway? My version of .NET doesn't have this method. Is it an extension method you wrote?
  • CSharpNoob
    CSharpNoob over 13 years
    yes I wrote that extension method it has List.FindIndex() inside then it removes the item by the List.RemoveAt() method. like on my example, it only needs to know the string ID to remove the item on the list unlike the built in Remove/RemoveAll method, you have to pass an Instance of the item.
  • Mark Byers
    Mark Byers over 13 years
    @CSharpNoob: RemoveAll doesn't need to be passed an instance of the item -it needs only to be passed a predicate (function) that decides whether or not each item should be removed.
  • CSharpNoob
    CSharpNoob over 13 years
    public static void RemoveItem(this List<Item> items, string itemId) { if (items!= null) { int itemIndex = items.FindIndex(delegate(Item b) { return b.ID.Equals(itemId, StringComparison.Ordinal); }); if (!(itemIndex < 0)) { items.RemoveAt(itemIndex); } } }
  • CSharpNoob
    CSharpNoob over 13 years
    @mark oh yeah, only the Remove method not the RemoveAll, however even if I replace RemoveItem with RemoveAll, I still need to iterate because of the ID parameter.
  • CSharpNoob
    CSharpNoob over 13 years
    now I realize, RemoveAll and my RemoveItem method functions the same, thanks dude.
  • Mark Byers
    Mark Byers over 13 years
    @CSharpNoob: See this very similar question: stackoverflow.com/questions/4312437/…
  • CSharpNoob
    CSharpNoob over 13 years
    wow works like a charm: HashSet<string> itemIds = new HashSet<string>(((List<Item>)ViewState["ItemsToRemove"]).Sel‌​ect(s => s.ID)); saleItems.RemoveAll(c => itemIds.Contains(c.ID));
  • Mark Byers
    Mark Byers over 13 years
    @CSharpNoob: Yes, exactly. I've added your code to my answer and rewritten it slightly to make it more readable. Hope that's OK.
  • Pauli Østerø
    Pauli Østerø over 13 years
    reading your blogpost i fail to see why its really THAT odd... all you have to accept is that the select-clause will call the Select method on the "thing" specified in the in-clause. The compiler can't bother to care what the thing is, as long as it has a Select method.
  • Jon Skeet
    Jon Skeet over 13 years
    @Pauli: It doesn't have have to be a Select method though - it can be a property or field of a delegate type. You don't think that's even slightly weird? Likewise the fact that the source can just be the name of a type (so that a static method gets called) seems pretty odd to me.
  • Pauli Østerø
    Pauli Østerø over 13 years
    Defining a func to a property/field basically makes it callable with the syntax Select(), tricking the caller to make it look like a method, so i don't find it so weird. And if the compiler doesn't bother with what thing we're specifying in the in-clause, a delegate, a type of a variable is all acceptable as long as they have a Select method or property/field named Select of the type Func.