28

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} }
Gaurang Dave
  • 3,956
  • 2
  • 15
  • 34
user160677
  • 4,233
  • 12
  • 42
  • 55

7 Answers7

48

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;
}
Alex
  • 7,639
  • 3
  • 45
  • 58
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] }  );
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 1
    Can someone explain what this does? It's quite dense. – GameKyuubi Dec 16 '16 at 07: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 `i`th element at each iteration, then `B` gets the `i+1`th element. – tvanfosson Dec 16 '16 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 Feb 23 '23 at 08: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));
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
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});
Abhijeet Nagre
  • 876
  • 1
  • 11
  • 21
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;
            }
        }
    }
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()));
A K
  • 714
  • 17
  • 39
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;
    }
}
Benjol
  • 63,995
  • 54
  • 186
  • 268