Reputation: 290
Seems like I'm stuck once again with recursive algorithms...
My application is supposed to sort files to different folders, according to the information specified by the user and according to a subfolder structure represented by a string like the following:
[ROOT] \ brand \ color \ material \
The tags in the structure string represent collections:
Assume:
var brand = new List<string> { "Nike", "Adidas", "Reebok" };
var color = new List<string> { "red", "blue", "yellow", "black" };
var material = new List<string> { "leather", "fabric" };
var data = new List<List<string>>() { brand, color, material };
And what I 'm trying to get is something like:
[ROOT]\Nike\red\leather
[ROOT]\Nike\red\fabric
[ROOT]\Nike\blue\leather
[ROOT]\Nike\blue\fabric
[ROOT]\Nike\yellow\leather
[ROOT]\Nike\yellow\fabric
[ROOT]\Nike\black\leather
[ROOT]\Nike\black\fabric
[ROOT]\Adidas\red\leather
[ROOT]\Adidas\red\fabric
[ROOT]\Adidas\blue\leather
[ROOT]\Adidas\blue\fabric
[ROOT]\Adidas\yellow\leather
[ROOT]\Adidas\yellow\fabric
[ROOT]\Adidas\black\leather
[ROOT]\Adidas\black\fabric
[ROOT]\Reebok\red\leather
[ROOT]\Reebok\red\fabric
[ROOT]\Reebok\blue\leather
[ROOT]\Reebok\blue\fabric
[ROOT]\Reebok\yellow\leather
[ROOT]\Reebok\yellow\fabric
[ROOT]\Reebok\black\leather
[ROOT]\Reebok\black\fabric
The problem is that the amount of data tags (brand, color, material) and their order is not known in advance, hence the need for recursion.
Any idea?
Thank you so much in advance!
Upvotes: 1
Views: 651
Reputation: 3624
Here is the simple way by cross joining the current result with each member of the next one. For this, you also need to create any array that holds [ROOT].
var root = new List<string> { "[ROOT]" };
var brand = new List<string> { "Nike", "Adidas", "Reebok" };
var color = new List<string> { "red", "blue", "yellow", "black" };
var material = new List<string> { "leather", "fabric" };
var data = new List<List<string>>() { root, brand, color, material };
IEnumerable<string> lstComb = new List<string> { null };
foreach (var list in data)
{
lstComb = lstComb.SelectMany(o => list.Select(s => $"{o}\\{s}".TrimStart(new char[]{'\\'})));
}
Note: Order must be preserved when adding items to the data list that holds lists.
Upvotes: 0
Reputation: 116098
Here is the code By Eric Lippert. Cartesian Product..
http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
// base case:
IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };
foreach (var sequence in sequences)
{
var s = sequence; // don't close over the loop variable
// recursive case: use SelectMany to build the new product out of the old one
result =
from seq in result
from item in s
select seq.Concat(new[] { item });
}
return result;
}
var result = CartesianProduct(new List<List<string>>() {brand,color,material });
Usage Example:
var brand = new List<string> { "Nike", "Adidas", "Reebok" };
var color = new List<string> { "red", "blue", "yellow", "black" };
var material = new List<string> { "leather", "fabric" };
foreach (var row in CartesianProduct(new List<List<string>>() { brand, color, material }))
{
Console.WriteLine(String.Join(",", row));
}
Upvotes: 8