假设我有以下类 - ItemMenu - 带有列表列表。 如何使用 C# 生成包含所有可用组合的交叉连接输出?
对于以下代码,我期望 3(温度)乘以 4(侧面)乘以 4(饮料)结果,例如:
- 稀有牛排,没有配菜,没有饮料
- 稀有牛排,没有配菜和啤酒
- 稀有牛排,无配菜和 Wine
- 稀有牛排,无配菜和可乐
- 生牛排配沙拉,不含饮料
- ...(共48种组合)
显然,修饰符和修饰符选项的数量事先是未知的,所以如果我们有 4 个修饰符,每个修饰符有 5 个选项,我们最终会得到 5*6*6*6 (第一个是强制性的,其余的没有添加选项)结果。我正在考虑使用 LINQ SelectMany 展平列表,但我无法使用未知数量的选项产生预期结果。我正在考虑将所有选项记录为数组中的位标志,然后进行累加,但存在强制标志问题。
public class ItemMenu
{
public string Name { get; set; }
public List<Modifier> Modifiers { get; set; }
}
public class Modifier
{
public bool IsMandatory { get; set; }
public string Name { get; set; }
public List<ModifierOption> Options { get; set; }
}
public class ModifierOption
{
public int ID { get; set; }
public string Name { get; set; }
public bool Selected { get; set; }
}
public static ItemMenu GetSteakMenu()
{
return new ItemMenu
{
Name = "Beef Steak",
Modifiers = new List<Modifier> {
new Modifier { Name = "Temperature", IsMandatory = true, Options = new List<ModifierOption>
{
new ModifierOption { ID = 1, Name = "Rare" },
new ModifierOption { ID = 2, Name = "Medium" },
new ModifierOption { ID = 3, Name = "Well done" },
}
},
new Modifier { Name = "Side", Options = new List<ModifierOption>
{
new ModifierOption { ID = 1, Name = "Salad" },
new ModifierOption { ID = 2, Name = "Fries" },
new ModifierOption { ID = 3, Name = "Sweet fries" },
}
},
new Modifier { Name = "Drink", Options = new List<ModifierOption>
{
new ModifierOption { ID = 1, Name = "Beer" },
new ModifierOption { ID = 2, Name = "Wine" },
new ModifierOption { ID = 3, Name = "Coke" },
}
}
}
};
}
对于输出类型,我最好使用 ItemMenu 对象列表,并将 ModifierOptions 标志设置为 true,但任何类型的输出对象都是可接受的,甚至是字符串。 谢谢你!
最佳答案
回答标题中的问题,使用 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}))));
}
据我了解,您想这样使用它:
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
编辑:将累加器更改为使用 Enumerable.Empty<T>()
而不是实例化数组,因为它避免了分配。
关于c# - LINQ 交叉连接列表列表?未知数量列表的笛卡尔积,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58273898/