c# - LINQ 交叉连接列表列表?未知数量列表的笛卡尔积

标签 c# linq cross-join

假设我有以下类 - 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/

相关文章:

asp.net - 使用 LINQ 连接 2 个列表?

r - 总结自连接索引,同时避免 R data.table 中的笛卡尔积

C# 程序集在子域中创建实例

c# - Entity Framework 在应该执行内部联接时生成左外部联接(多个表)

c# - 如何增加 SQL 连接字符串的连接超时?

c# - 如何使用 C# 中的 QBO API v3 将付款应用于发票?

c# - EF 6 Plus - 如何进行 future 的原始查询

c# - 有没有更好的方法来返回列表中的下一项并从末尾循环到前面?

SAS:提高交叉联接的效率

sql - 使用 2 个 ORDER BY 语句的 COUNT 中没有零值