29

When i have a list

IList<int> list = new List<int>();
list.Add(100);
list.Add(200);
list.Add(300);
list.Add(400);
list.Add(500);

What is the way to extract a pairs

Example : List elements {100,200,300,400,500}

Expected Pair : { {100,200} ,{200,300} ,{300,400} ,{400,500} }
3
  • Please specify your problem better. What you have now can mean one of any number of things...
    – leppie
    Commented Oct 26, 2009 at 11:45
  • Do you want to operate on a raw IEnumerable or only an IList<T>? If so, see my edit.
    – SLaks
    Commented Oct 26, 2009 at 12:02
  • Does this answer your question? Split List into Sublists with LINQ
    – malat
    Commented May 15 at 11:32

7 Answers 7

50

The most elegant way with LINQ: list.Zip(list.Skip(1), Tuple.Create)

A real-life example: This extension method takes a collection of points (Vector2) and produces a collection of lines (PathSegment) needed to 'join the dots'.

static IEnumerable<PathSegment> JoinTheDots(this IEnumerable<Vector2> dots)
{
    var segments = dots.Zip(dots.Skip(1), (a,b) => new PathSegment(a, b));
    return segments;
}
0
33

This will give you an array of anonymous "pair" objects with A and B properties corresponding to the pair elements.

var pairs = list.Where( (e,i) => i < list.Count - 1 )
                .Select( (e,i) => new { A = e, B = list[i+1] }  );
3
  • 1
    Can someone explain what this does? It's quite dense.
    – GameKyuubi
    Commented Dec 16, 2016 at 7:25
  • 1
    @GameKyuubi the (e,i) notation represents the signature that takes both the element and the index of the element. The Where clause allows all but the last element in the sequence. The Select clause creates a new anonymous object with the A element assigned the original element in the sequence and the B element assigned the following element in the sequence. That is, if e represents the ith element at each iteration, then B gets the i+1th element.
    – tvanfosson
    Commented Dec 16, 2016 at 14:21
  • 1
    Nowadays to make the code a bit more self explaining, you could replace .Where( (e,i) => i < list.Count - 1 ) with .SkipLast(1).
    – Jensen
    Commented Feb 23, 2023 at 8:43
9

You can use a for loop:

var pairs = new List<int[]>();
for(int i = 0; i < list.Length - 1; i++)
    pairs.Add(new [] {list[i], list[i + 1]);

You can also use LINQ, but it's uglier:

var pairs = list.Take(list.Count - 1).Select((n, i) => new [] { n, list[i + 1] });

EDIT: You can even do it on a raw IEnumerable, but it's much uglier:

var count = list.Count();
var pairs = list
    .SelectMany((n, i) => new [] { new { Index = i - 1, Value = n }, new { Index = i, Value = n } })
    .Where(ivp => ivp.Index >= 0 && ivp.Index < count - 1)    //We only want one copy of the first and last value
    .GroupBy(ivp => ivp.Index, (i, ivps) => ivps.Select(ivp => ivp.Value));
0
3

More general would be:

    public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> values, int count, Func<TSource[], TResult> pairCreator)
    {
        if (count < 1) throw new ArgumentOutOfRangeException("count");
        if (values == null) throw new ArgumentNullException("values");
        if (pairCreator == null) throw new ArgumentNullException("pairCreator");
        int c = 0;
        var data = new TSource[count];
        foreach (var item in values)
        {
            if (c < count)
                data[c++] = item;
            if (c == count)
            {
                yield return pairCreator(data);
                c = 0;
            }
        }
    }
3

Following solution uses zip method. Zip originalList and originalList.Skip(1) so that one gets desired result.

    var adjacents =
            originalList.Zip(originalList.Skip(1),
                             (a,b) => new {N1 = a, N2 = b});
2

Using .Windowed() from MoreLINQ:

var source = new[] {100,200,300,400,500};
var result = source.Windowed(2).Select(x => Tuple.Create(x.First(),x.Last()));
0

Off the top of my head and completely untested:

public static T Pairwise<T>(this IEnumerable<T> list)
{
    T last;
    bool firstTime = true;
    foreach(var item in list)
    {
        if(!firstTime) 
            return(Tuple.New(last, item));
        else 
            firstTime = false; 
        last = item;
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.