LINQ cross join list of lists? Cartesian product for unknown number of lists
Answering the question in the title, a product of an unknown number of lists using LINQ:
public static class EnumerableExtensions
{
public static IEnumerable<IEnumerable<T>> CrossProduct<T>(
this IEnumerable<IEnumerable<T>> source) =>
source.Aggregate(
(IEnumerable<IEnumerable<T>>) new[] { Enumerable.Empty<T>() },
(acc, src) => src.SelectMany(x => acc.Select(a => a.Concat(new[] {x}))));
}
As far as I understand you want to use it like this:
var beefSteak = GetSteakMenu();
var modifiers = beefSteak.Modifiers.Select(m => m.Options);
var results = modifiers.CrossProduct();
foreach (var resultList in results)
{
Console.WriteLine($"Steak, {string.Join(", ", resultList.Select(r => r.Name))}");
}
> Steak, Rare, Salad, Beer
> Steak, Medium, Salad, Beer
> Steak, Well done, Salad, Beer
> Steak, Rare, Fries, Beer
> Steak, Medium, Fries, Beer
> Steak, Well done, Fries, Beer
> Steak, Rare, Sweet fries, Beer
> Steak, Medium, Sweet fries, Beer
> Steak, Well done, Sweet fries, Beer
> Steak, Rare, Salad, Wine
> Steak, Medium, Salad, Wine
> Steak, Well done, Salad, Wine
> Steak, Rare, Fries, Wine
> Steak, Medium, Fries, Wine
> Steak, Well done, Fries, Wine
> Steak, Rare, Sweet fries, Wine
> Steak, Medium, Sweet fries, Wine
> Steak, Well done, Sweet fries, Wine
> Steak, Rare, Salad, Coke
> Steak, Medium, Salad, Coke
> Steak, Well done, Salad, Coke
> Steak, Rare, Fries, Coke
> Steak, Medium, Fries, Coke
> Steak, Well done, Fries, Coke
> Steak, Rare, Sweet fries, Coke
> Steak, Medium, Sweet fries, Coke
> Steak, Well done, Sweet fries, Coke
EDIT: Changed the accumulator to use Enumerable.Empty<T>()
instead of instantiating an array, as it avoids an allocation.
How to get a Cartesian product of a list containing lists?
Here is a LINQ extension method using Aggregate
:
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences) =>
sequences.Aggregate(
Enumerable.Empty<T>().AsSingleton(),
(accumulator, sequence) => accumulator.SelectMany(
accseq => sequence,
(accseq, item) => accseq.Append(item)));
You need the extension method AsSingleton
:
public static IEnumerable<T> AsSingleton<T>(this T item) => new[] { item };
This is based on this answer from @EricLippert.
Merge data into single Dictionary after cross join of two list in c#
Perhaps you mean you want your output "row" to be a single dictionary containing all of x and all of y. It could be like:
var result= list1.SelectMany(
x=> list2,
(x,y) => new Dictionary<WhateverType,YouHave>(x.Concat(Y))
);
It takes all of the KeyValuePair<WhateverTypr,YouHave>
in x and y and makes an IEnumerable<KeyValuePair<WhateverType,YouHave>>
from them, then makes a new Dictionary that contains all those keyvaluepairs
The types of the dictionary will need to be consistent; you can't have x be a Dictionary<string,int>
and y be a Dictionary<int,string>
for example
Dynamically cross-join multiple different-size collections together in Linq (C#)
You could create an extension method like the following:
public static class EnumerableExtensions
{
public static IEnumerable<TValue []> Permutations<TKey, TValue>(this IEnumerable<TKey> keys, Func<TKey, IEnumerable<TValue>> selector)
{
var keyArray = keys.ToArray();
if (keyArray.Length < 1)
yield break;
TValue [] values = new TValue[keyArray.Length];
foreach (var array in Permutations(keyArray, 0, selector, values))
yield return array;
}
static IEnumerable<TValue []> Permutations<TKey, TValue>(TKey [] keys, int index, Func<TKey, IEnumerable<TValue>> selector, TValue [] values)
{
Debug.Assert(keys.Length == values.Length);
var key = keys[index];
foreach (var value in selector(key))
{
values[index] = value;
if (index < keys.Length - 1)
{
foreach (var array in Permutations(keys, index+1, selector, values))
yield return array;
}
else
{
yield return values.ToArray(); // Clone the array;
}
}
}
}
As an example, it could be used like:
public static void TestPermutations()
{
int [][] seqence = new int [][]
{
new int [] {1, 2, 3},
new int [] {101},
new int [] {201},
new int [] {301, 302, 303},
};
foreach (var array in seqence.Permutations(a => a))
{
Debug.WriteLine(array.Aggregate(new StringBuilder(), (sb, i) => { if (sb.Length > 0) sb.Append(","); sb.Append(i); return sb; }));
}
}
and produce the following output:
1,101,201,301
1,101,201,302
1,101,201,303
2,101,201,301
2,101,201,302
2,101,201,303
3,101,201,301
3,101,201,302
3,101,201,303
Is that what you want?
c# cross join two same type of lists
You can use the Join (you can also use a dictionary, but I'm not going to show it):
Here's the syntax for join:
var test = someOfThem.Join(all, item => item.Id, element => element.Id,
(item, element) => new Person {
Id = item.Id ?? element.Id,
Name = item.Name ?? element.Name,
Age = item.Age ?? element.Age
});
Cartesian product two lists
This is a quick fix:
let rec cartesian = function
| ([],[]) -> []
| (xs,[]) -> []
| ([],ys) -> []
| (x::xs, ys) -> (List.map(fun y -> x,y) ys) @ (cartesian (xs,ys))
The idea is that with each element x
, you generate a list [(x, y1); (x, y2); ...; (x, yn)]
and concatenate those lists altogether.
In your function, the first pattern matching case is redundant. And the arguments are more convenient to be in the curried form. The function could look like this:
let rec cartesian xs ys =
match xs, ys with
| _, [] -> []
| [], _ -> []
| x::xs', _ -> (List.map (fun y -> x, y) ys) @ (cartesian xs' ys)
Once you understand the idea, you can see that the high-order function List.collect
perfectly matches the task:
let cartesian xs ys =
xs |> List.collect (fun x -> ys |> List.map (fun y -> x, y))
Related Topics
Recommendations on Parsing .Eml Files in C#
How to Add Parameters into a Webrequest
Determine List of Event Handlers Bound to Event
Convert Datetime to Julian Date in C# (Tooadate Safe)
How to Optionally Turn Off the JSONignore Attribute at Runtime
The Remote Certificate Is Invalid According to the Validation Procedure
With Unity How to Inject a Named Dependency into a Constructor
Iterating Over JSON Object in C#
Entity Framework Filter "Expression<Func<T, Bool>>"
How to Xml-Serialize a Dictionary
Get the Default Timezone for a Country (Via Cultureinfo)
How Can a C++ Windows Dll Be Merged into a C# Application Exe
A Textbox/Richtextbox That Has Syntax Highlighting? [C#]
Reading Emails from Gmail in C#