21

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?

Community
  • 1
  • 1
griegs
  • 22,624
  • 33
  • 128
  • 205

7 Answers7

53

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.

Jeremy Foster
  • 4,673
  • 3
  • 30
  • 49
17

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();
Abdul Saboor
  • 4,079
  • 2
  • 33
  • 25
  • I think `Reverse` is not implemented in SQL Server. – Luke Vo Mar 16 '14 at 05:53
  • http://msdn.microsoft.com/en-us/library/bb358497(v=vs.110).aspx It is LINQ and Reverse is here – Abdul Saboor Apr 09 '14 at 08:55
  • Yes, it's LINQ API, but if it's LINQ to SQL (Server), it will throw error, because there is no equivalence in SQL. – Luke Vo Apr 09 '14 at 10:25
  • 2
    Yes! you are right it will throw an error in case of LINQ to SQL.but i dont think here we are talking about LINQ to SQL. Let me edit my answer so people should know about it. – Abdul Saboor Apr 09 '14 at 12:01
  • Works perfectly with `ICollection`. I adapted this to get only one result, with an object: `list.SkipWhile(x => !x.Equals(myObject)).Skip(1).Take(1).SingleOrDefault()`. – Emy Blacksmith Feb 26 '19 at 14:11
16

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.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
EMP
  • 59,148
  • 53
  • 164
  • 220
7

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);
Jim Johnson
  • 1,153
  • 1
  • 10
  • 5
1

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.

Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
1

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);
Alex Logvin
  • 766
  • 10
  • 12
0

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);
spoulson
  • 21,335
  • 15
  • 77
  • 102
  • I dont think this will exactly work. In the OPs case it would seem that the original would only return 1 result, and they want the next in the ORIGINAL list. – Adriaan Stander Apr 21 '10 at 04:13
  • @spoulson, that's correct. I want to find the matching item and then return the next item in the list. – griegs Apr 21 '10 at 04:14