15

I have a dictionary of projects and if I select a project then I will give an option previous and next. I have added a code example but I hope there is a better / faster way to do this e.g. for 500 projects.

Is there maybe a LINQ option or something?

I have checked Enumerator but it only has a moveNext method and can't set the current.

Quick example:

projects is a Dictionary.

project is a KeyValuePair that exists in the Dictionary.

var match = false;
var save = new KeyValuePair<ExtendedProjectLightPlan, Page>();
var before = new KeyValuePair<ExtendedProjectLightPlan, Page>();
var after = new KeyValuePair<ExtendedProjectLightPlan, Page>();
foreach (var p in projects)
{
    before = save;
    save = p;

    if (match)
    {
        after = p;
        break;
    }

    if (p.Key.Id == project.Key.Id)
    {
        match = true;
    }                
}
Pac0
  • 21,465
  • 8
  • 65
  • 74
Marco
  • 2,306
  • 2
  • 26
  • 43

5 Answers5

18

Item before 'current':

items.TakeWhile(x => x != current).LastOrDefault();

Item after 'current':

items.SkipWhile(x => x != current).Skip(1).FirstOrDefault();

Works well for integral types but will return default(T) at the ends of the sequence. It might be useful to cast the items to Nullable<T> so that before the first item, and after the last item return null instead.

Damian Powell
  • 8,655
  • 7
  • 48
  • 58
  • 1
    For a list, this is the best solution. – FranzHuber23 Mar 20 '19 at 09:34
  • This returns the Last item for "Before" if the current is not in the collection. I ended up using `items.Contains(current) ? items.TakeWhile(x => x != current).LastOrDefault() : default(current);` – DharmaTurtle Oct 16 '19 at 12:02
  • Or, `items.AsEnumerable().Reverse().SkipWhile(x => x != current).Skip(1).FirstOrDefault()` lol. The `AsEnumerable()` is due to https://stackoverflow.com/a/26241613 – DharmaTurtle Oct 16 '19 at 12:21
16

Have you tried using IndexOf() and ElementAt() methods??

    Int32 index = list1.IndexOf(item);
    var itemPrev = list1.ElementAt(index - 1);
    var itemNext = list1.ElementAt(index + 1);
decyclone
  • 30,394
  • 6
  • 63
  • 80
14

There's nothing built into LINQ to do this, but you could write your own fairly easily... here's an implementation which uses Tuple from .NET 4. It will return n-2 items for a sequence which originally has n items - but you could adjust that if necessary.

public IEnumerable<Tuple<T, T, T>> WithNextAndPrevious<T>
    (this IEnumerable<T> source)
{
    // Actually yield "the previous two" as well as the current one - this
    // is easier to implement than "previous and next" but they're equivalent
    using (IEnumerator<T> iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            yield break;
        }
        T lastButOne = iterator.Current;
        if (!iterator.MoveNext())
        {
            yield break;
        }
        T previous = iterator.Current;
        while (iterator.MoveNext())
        {
            T current = iterator.Current;
            yield return Tuple.Create(lastButOne, previous, current);
            lastButOne = previous;
            previous = current;
        }
    }        
}

Note that as per LukeH's answer, dictionaries are unordered... but hopefully the above will help you anyway.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
5

Dictionaries have no intrinsic ordering, so the idea of previous and next items is pretty much nonsensical.

LukeH
  • 263,068
  • 57
  • 365
  • 409
1

I agree with the other comments with regard to ordering in dictionaries. But since dictionaries offer IEnumerable<KeyValuePair<K, V>> there is, at least, a small argument to say they have some sort of order. Anyway, here's my suggestion:

var ll = new LinkedList<ExtendedProjectLightPlan>();
var qs =
    from p in projects
    let node = ll.AddLast(p.Key)
    select new { Project = p, Node = node, };

var lookup = qs.ToDictionary(q => q.Project, q => q.Node);

var current = (ExtendedProjectLightPlan)null; //Whatever the current one is.

var previous = lookup[current].Previous.Value;
var next = lookup[current].Next.Value;

This should make it very simple to move from any project to the previous or next one - and it will be very very fast. (Although speed shouldn't be an issue since this is for UI, right?)

Enigmativity
  • 113,464
  • 11
  • 89
  • 172