Linq to return ALL pairs of elements from two lists?
Yes it is possible. Eric Lippert wrote a very good article on this topic:
Computing a Cartesian Product with LINQ
If you only have 2 lists, then you could directly use multiple from
like this:
from a in s1
from b in s2
select new [] { a, b};
or even:
s1.SelectMany(a => s2.Select(b => new [] { a, b }));
But the solution given by Eric Lippert in the previous article allows you to compute the cartesian product of several sequences. With the following extension method:
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] { item }));
}
You could write:
var l1 = new[] {1, 2};
var l2 = new[] {4, 5, 6};
var l3 = new[] {7, 3};
foreach (var result in new []{l1,l2,l3}.CartesianProduct())
{
Console.WriteLine("{"+string.Join(",",result)+"}");
}
And obtain:
{1,4,7}
{1,4,3}
{1,5,7}
{1,5,3}
{1,6,7}
{1,6,3}
{2,4,7}
{2,4,3}
{2,5,7}
{2,5,3}
{2,6,7}
{2,6,3}
C# & LINQ, Select two (consecutive) items at once
Another answer presents a nice and clean solution using LINQ's Skip
and Zip
.
It is absolutely correct, but I'd like to point out that it enumerates the source twice. That may or may not matter, depending on each individual use case. If it matters for your case, here's a longer alternative that is functionally equivalent but enumerates the source once:
static class EnumerableUtilities
{
public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> selector)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (selector == null) throw new ArgumentNullException(nameof(selector));
return SelectTwoImpl(source, selector);
}
private static IEnumerable<TResult> SelectTwoImpl<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> selector)
{
using (var iterator = source.GetEnumerator())
{
var item2 = default(TSource);
var i = 0;
while (iterator.MoveNext())
{
var item1 = item2;
item2 = iterator.Current;
i++;
if (i >= 2)
{
yield return selector(item1, item2);
}
}
}
}
}
Example:
var seq = new[] {"A", "B", "C", "D"}.SelectTwo((a, b) => a + b);
The resulting sequence contains "AB"
, "BC"
, "CD"
.
How can I pair up two items from a list using linq?
In .NET 6, you can use LINQ Chunk<TSource>(int size)
.
IEnumerable<string[]> groups = list.Chunk(2);
Using LINQ to create pairs from two different list where the entries have same attribute
Just join both sequences on RequestId and create CallPair from joined items:
var requestWithMatches = from req in _requests
join resp in _responses
on req.RequestId equals resp.RequestId
select new CallPair(req, resp);
Group items in pairs
Well, you could do:
var pairs = sequence.Select((value, index) => new { value, index } )
.GroupBy(x => x.index / 2, x => x.value)
The result is an IGrouping<int, T>
with a key of 0, 1, 2 etc and the contents of each group being one or two items.
However, I'd possibly write a custom extension method:
public static IEnumerable<Tuple<T, T>> PairUp<T>(this IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
while (iterator.MoveNext())
{
var first = iterator.Current;
var second = iterator.MoveNext() ? iterator.Current : default(T);
yield return Tuple.Create(first, second);
}
}
}
This will yield a sequence of tuples - the downside here is that the final tuple will have the default value for T
as the "second" item if the sequence has an odd number of items. For reference types where the sequence only consists of non-null values, that's okay, but for some sequences it wouldn't help.
Getting pair-set using LINQ
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] } );
Get all pairs in a list using LINQ
Slight reformulation of cgeers answer to get you the tuples you want instead of arrays:
var combinations = from item1 in list
from item2 in list
where item1 < item2
select Tuple.Create(item1, item2);
(Use ToList
or ToArray
if you want.)
In non-query-expression form (reordered somewhat):
var combinations = list.SelectMany(x => list, (x, y) => Tuple.Create(x, y))
.Where(tuple => tuple.Item1 < tuple.Item2);
Both of these will actually consider n2 values instead of n2/2 values, although they'll end up with the correct answer. An alternative would be:
var combinations = list.SelectMany((x, i) => list.Skip(i + 1), (x, y) => Tuple.Create(x, y));
... but this uses Skip
which may also not be optimized. It probably doesn't matter, to be honest - I'd pick whichever one is most appropriate for your usage.
How to use LINQ to find all combinations of n items from a set of numbers?
Usage:
var results = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.DifferentCombinations(3);
Code:
public static class Ex
{
public static IEnumerable<IEnumerable<T>> DifferentCombinations<T>(this IEnumerable<T> elements, int k)
{
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).DifferentCombinations(k - 1).Select(c => (new[] {e}).Concat(c)));
}
}
Related Topics
Create Http Post Request and Receive Response Using C# Console Application
Reverse Sorted Dictionary in .Net
Extension Method on Enumeration, Not Instance of Enumeration
Piping in a File on the Command-Line Using System.Diagnostics.Process
Can't See Localhost from Uwp App
MVC 5 Dynamic Rows with Begincollectionitem
Multiple Fields Validation Using Remote Validation
Generic List of Generic Objects
Get List<> Element Position in C# Using Linq
Show Authentication Dialog in C# for Windows Vista/7
How to Connect to a Ms Access File (Mdb) Using C#
Why am I Getting Sehexception When Calling Roleenvironment.Getconfigurationsettingvalue("Mykey")