如何从属性选项列表生成所有唯一的Product变体?
生成全属性组合的Product变体列表
现有模型定义
PayloadItem 模型
internal class PayloadItem { public string Attribute { get; set; } public string Option { get; set; } }
Product 及 AttributeValue 模型
internal class Product { public string Name { get; set; } public string Description { get; set; } public List<AttributeValue> AttributeValues { get; set; } } internal class AttributeValue { public string Attribute { get; set; } public string Value { get; set; } }
输入的属性选项列表
List<PayloadItem> items = new List<PayloadItem>() { new PayloadItem{Attribute="Size",Option="S"}, new PayloadItem{Attribute="Size",Option="M"}, new PayloadItem{Attribute="Color",Option="Red"}, new PayloadItem{Attribute="Color",Option="Green"}, new PayloadItem{Attribute="Color",Option="Blue"}, new PayloadItem{Attribute="Width",Option="100"}, new PayloadItem{Attribute="Width",Option="200"}, new PayloadItem{Attribute="Length",Option="10000"}, new PayloadItem{Attribute="Length",Option="20000"}, new PayloadItem{Attribute="Length",Option="30000"}, };
核心需求
- 生成所有可能的
Product对象列表,每个Product必须包含所有属性的一个取值 - 任意两个
Product的AttributeValues至少有一个属性值不同 Product的Name、Description等其他属性直接复用外部传入的对象属性,无需额外处理
实现方案
通过生成属性选项的笛卡尔积,再映射为Product对象来完成需求,具体步骤如下:
代码实现
// 1. 按属性名称分组,整理每个属性对应的所有可选值 var groupedAttributes = items .GroupBy(item => item.Attribute) .Select(g => new { Attribute = g.Key, Options = g.Select(item => item.Option).ToList() }) .ToList(); // 2. 生成所有属性选项的笛卡尔积组合 var attributeCombinations = GetCartesianProduct(groupedAttributes); // 3. 映射为Product变体(假设外部主产品为externalProduct) var externalProduct = new Product { Name = "示例产品", Description = "示例描述" }; var productVariants = attributeCombinations .Select(combination => new Product { Name = externalProduct.Name, Description = externalProduct.Description, AttributeValues = combination.Select(c => new AttributeValue { Attribute = c.Attribute, Value = c.Option }).ToList() }) .ToList(); // 辅助方法:递归生成笛卡尔积 private static IEnumerable<IEnumerable<(string Attribute, string Option)>> GetCartesianProduct(List<dynamic> groups) { if (groups.Count == 0) { yield return Enumerable.Empty<(string, string)>(); yield break; } var firstGroup = groups[0]; var remainingGroups = groups.Skip(1).ToList(); foreach (var option in firstGroup.Options) { foreach (var combination in GetCartesianProduct(remainingGroups)) { yield return new[] { (firstGroup.Attribute, option) }.Concat(combination); } } }
代码说明
- 属性分组:将原始
PayloadItem列表按Attribute归类,明确每个属性的可选值集合 - 笛卡尔积生成:通过递归方法遍历所有属性的可选值,生成包含所有属性的完整组合
- 变体映射:将每个属性组合转换为
AttributeValue列表,同时复用外部产品的其他属性,得到最终的变体产品列表
示例输出(简化版)
[ { "Name": "示例产品", "Description": "示例描述", "AttributeValues": [ {"Attribute": "Size", "Value": "S"}, {"Attribute": "Color", "Value": "Red"}, {"Attribute": "Width", "Value": "100"}, {"Attribute": "Length", "Value": "10000"} ] }, { "Name": "示例产品", "Description": "示例描述", "AttributeValues": [ {"Attribute": "Size", "Value": "S"}, {"Attribute": "Color", "Value": "Red"}, {"Attribute": "Width", "Value": "100"}, {"Attribute": "Length", "Value": "20000"} ] }, // ... 其余所有属性组合变体 ]
内容的提问来源于stack exchange,提问作者Mead Alsorani
相关产品推荐
相关产品推荐

