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