user160677
user160677

Reputation: 4303

Getting pair-set using LINQ

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} }

Upvotes: 30

Views: 16212

Answers (7)

tvanfosson
tvanfosson

Reputation: 532435

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

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.

Upvotes: 33

A K
A K

Reputation: 764

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

Upvotes: 2

Alex
Alex

Reputation: 7919

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

Upvotes: 51

Abhijeet Nagre
Abhijeet Nagre

Reputation: 916

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

Upvotes: 3

Daniel Gro&#223;
Daniel Gro&#223;

Reputation: 31

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

Upvotes: 3

SLaks
SLaks

Reputation: 887365

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

Upvotes: 9

Benjol
Benjol

Reputation: 66531

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

Upvotes: 0

Related Questions