开发者

LINQ Next Item in List

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 t开发者_JS百科o return item 7 instead.

Or is there a better way?


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.


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.


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();


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);


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.


If you are sure that:

  • the item is unique in the list
  • the item is gotten from the same list
  • there is the next item

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);


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);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜