griegs
griegs

Reputation: 22770

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 to return item 7 instead.

Or is there a better way?

Upvotes: 24

Views: 43600

Answers (7)

Alex Logvin
Alex Logvin

Reputation: 881

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

Upvotes: 1

EMP
EMP

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

Abdul Saboor
Abdul Saboor

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

Jim Johnson
Jim Johnson

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

Jeremy Foster
Jeremy Foster

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

Anthony Pegram
Anthony Pegram

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

spoulson
spoulson

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

Related Questions