Reputation: 22770
Taking a look at my question HERE, I now want to return the next recommendation object (after) the one that matches the criteria.
So say I found item 6 out of 10, I'd like the query to return item 7 instead.
Or is there a better way?
Upvotes: 24
Views: 43600
Reputation: 881
If you are sure that:
you can get the next item this way
myList[myList.IndexOf(item) + 1];
// or
myList.ElementAt(myList.IndexOf(item) + 1);
If you're not sure there is the next item you can use try
+ catch
or:
myList.ElementAtOrDefault(myList.IndexOf(item) + 1);
Upvotes: 1
Reputation: 62031
Since you have a List<T>
object you can use its FindIndex
method instead of Where
to get the index of the first matching item rather than the item itself:
int index = recommendations.FindIndex(rp =>
rp.Products.Any(p => p.Product.Code == "A")
&& rp.Products.Any(p => p.Product.Code == "B")
);
Once you have the index you can get the next item or previous item or whatever you want.
Upvotes: 16
Reputation: 4127
Try this one
NEXT Item
MyList.SkipWhile(x => x != value).Skip(1).FirstOrDefault();
PREVIOUS Item note:Reverse() will not work for LINQ to SQL
var MyList2 = MyList.ToList();
MyList2.Reverse();
MyList2.SkipWhile(x => x != value).Skip(1).FirstOrDefault();
Upvotes: 17
Reputation: 1223
How about something like this:
public static class Extension
{
public static T Next<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
bool flag = false;
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
if (flag) return enumerator.Current;
if(predicate(enumerator.Current))
{
flag = true;
}
}
}
return default(T);
}
}
You could then call it like you would for Where
Products.Next(x => x.Whatever);
Upvotes: 7
Reputation: 4773
Here's my current best method:
MyList.SkipWhile(item => item.Name != "someName").Skip(1).FirstOrDefault()
An earlier answer uses Skip(1).Take(1)
which works, but returns a list of one result. In my case (and perhaps the OP's case), we're looking for the actual item. So my code skips until it gets to the one we're looking for (a Where would return a subset so we wouldn't have access to the next item) then skips one more and then gets the item.
Upvotes: 56
Reputation: 126952
This should do it. I haven't constructed a test for it specifically.
var nextProducts = from item1 in recommendations.Select((rec, idx) => new { Rec = rec, Index = idx })
join item2 in recommendations.Select((rec, idx) => new { Rec = rec, Index = idx })
on item1.Index equals item2.Index - 1
where item1.Rec.Products.Any(p => p.Code == "A")
&& item1.Rec.Products.Any(p => p.Code == "B")
select item2.Rec;
If you needed both records, the select statement could be
select new { MatchingItem = item1.Rec, NextItem = item2.Rec };
But then you would have to do a grouping to account for a matching item being the last item in the list (there would not be a next item in that case).
var nextProducts = from item1 in recommendations.Select((rec, idx) => new { Rec = rec, Index = idx })
join item2 in recommendations.Select((rec, idx) => new { Rec = rec, Index = idx })
on item1.Index equals item2.Index - 1
into groupjoin
from i2 in groupjoin.DefaultIfEmpty ()
where item1.Rec.Products.Any(p => p.Code == "A")
&& item1.Rec.Products.Any(p => p.Code == "B")
select new { MatchingItem = item1.Rec, NextItem = i2 == null ? null : i2.Rec };
The code I did test was something similar with a list of strings.
List<string> list = new List<string>() { "a", "b", "c", "a", "d", "a", "e" };
var query = from item1 in list.Select((s, idx) => new { Item = s, Index = idx })
join item2 in list.Select((s, idx) => new { Item = s, Index = idx })
on item1.Index equals item2.Index - 1
where item1.Item == "a"
select item2.Item;
Which returns b, d, and e.
Upvotes: 1
Reputation: 21601
Is item 7 a match to your Where clause? If so, use the Skip()
and Take()
extension methods:
var myProducts =
from rp in recommendations
where
cp.Products.Any(p => p.Product.Code == "A") &&
cp.Products.Any(p => p.Product.Code == "B")
select rp;
var nextProduct = myProducts.Skip(1).Take(1);
Upvotes: 0