Consider you have a List<Foo> of objects and Foo has an IsSelected property like so...
public class Foo
{
    public string Name{ get; set; }
    public bool IsSelected{ get; set; }
}
List<Foo> sourceItems = new List<Foo>
{
    new Foo(){ Name="First",   IsSelected=false},
    new Foo(){ Name="Second",  IsSelected=true },
    new Foo(){ Name="Third",   IsSelected=false},
    new Foo(){ Name="Fourth",  IsSelected=true },
    new Foo(){ Name="Fifth",   IsSelected=false},
    new Foo(){ Name="Sixth",   IsSelected=true },
    new Foo(){ Name="Seventh", IsSelected=true },
    new Foo(){ Name="Eighth",  IsSelected=false},
    new Foo(){ Name="Ninth",   IsSelected=false},
    new Foo(){ Name="Tenth",   IsSelected=false}
};
Using a Where clause, I can of course get just the items that are selected, like so...
var results = sourceItems.Where(item => item.IsSelected);
...but what if I want all items between the first and last items where IsSelected is true? (i.e. Second through Seventh)
I know I can use SkipWhile since it skips until the first true statement, then returns everything after...
// Returns from Second on
var results = sourceItems.SkipWhile(item => !item.IsSelected);
...and I know I can reverse and do the same again, but then I'd have to re-reverse at the end and double-reversing just feels like it would be unnecessarily expensive.
My other thought is to use the Select with index and store the last index where IsSelected is true, then use a Where clause at the end, checking if the index is below the last selected one, but that just seems expensive and cludgy.
int lastSelectedIndex = -1;
var results = sourceItems
    .SkipWhile(item => !item.IsSelected)
    .Select( (item, itemIndex) => 
    {
        if(item.IsSelected)
            lastSelectedIndex = index;
        return new {item, index};
    })
    .Where(anonObj => anonObj.index <= lastSelectedIndex)
    .Select(anonObj => anonObj.Item);
Alternately I think I can replace that last Where with a Take clause and just take the correct number of items so I wouldn't have to iterate the entire list, but I'm not sure lastSelectedIndex will have the correct value as I don't think Select returns the entire list, only the next enumeration, but I could be wrong.
.Take(lastSelectedIndex + 1);
So is there another way to do what I want?
Well, let me approach this differently by using indices and adding up the items to a new collection.
List<Foo> sourceItems = new List<Foo>
{
    new Foo(){ Name="First",   IsSelected=false},
    new Foo(){ Name="Second",  IsSelected=true },
    new Foo(){ Name="Third",   IsSelected=false},
    new Foo(){ Name="Fourth",  IsSelected=true },
    new Foo(){ Name="Fifth",   IsSelected=false},
    new Foo(){ Name="Sixth",   IsSelected=true },
    new Foo(){ Name="Seventh", IsSelected=true },
    new Foo(){ Name="Eighth",  IsSelected=false},
    new Foo(){ Name="Ninth",   IsSelected=false},
    new Foo(){ Name="Tenth",   IsSelected=false}
};
int startIndex = sourceItems.FindIndex(x => x.IsSelected);
int endIndex   = sourceItems.FindLastIndex(x => x.IsSelected);
var items = new List<Foo>();
for (int i = startIndex; i <= endIndex; i++)
    items.Add(sourceItems[i]);    
With 1 000 000 entries it only took 13 Milliseconds to get the result out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With