【问题标题】:Generate combinations of elements held in multiple list of strings in C#在 C# 中生成包含在多个字符串列表中的元素组合
【发布时间】:2015-02-19 15:23:12
【问题描述】:

我正在尝试自动化嵌套的 foreach,前提是存在一个主列表,其中包含字符串列表作为以下场景的项目。

例如,我有 5 个由主列表 lstMaster 持有的字符串列表

            List<string> lst1 = new List<string> { "1", "2" };
            List<string> lst2 = new List<string> { "-" };
            List<string> lst3 = new List<string> { "Jan", "Feb" };
            List<string> lst4 = new List<string> { "-" };
            List<string> lst5 = new List<string> { "2014", "2015" };

            List<List<string>> lstMaster = new List<List<string>> { lst1, lst2, lst3, lst4, lst5 };

            List<string> lstRes = new List<string>();



            foreach (var item1 in lst1)
            {
                foreach (var item2 in lst2)
                {
                    foreach (var item3 in lst3)
                    {
                        foreach (var item4 in lst4)
                        {
                            foreach (var item5 in lst5)
                            {
                                lstRes.Add(item1 + item2 + item3 + item4 + item5);
                            }
                        }
                    }
                }
            }

无论主列表 lstMaster 持有多少列表项,我都想自动执行以下 for 循环

【问题讨论】:

  • 如何将 List> 声明为外部父集合?
  • 你可以用两个内部 foreaches 解决它。
  • @user30... OP 已经有了这个
  • @user3021830 lstMaster 按照你的建议做
  • 非常相关的question。您需要使用的代码是var result = CartesianProduct(new List&lt;List&lt;string&gt;&gt;() { lst1, lst2, lst3, lst4, lst5 }) .Select(row =&gt; String.Join("", row)) .ToList();

标签: c# .net


【解决方案1】:

只需对每个连续列表进行交叉连接:

 IEnumerable<string> lstRes = new List<string> {null};
 foreach(var list in lstMaster)
 {
     // cross join the current result with each member of the next list
     lstRes = lstRes.SelectMany(o => list.Select(s => o + s));
 }

结果:

List<String> (8 items)
------------------------ 
1-Jan-2014 
1-Jan-2015 
1-Feb-2014 
1-Feb-2015 
2-Jan-2014 
2-Jan-2015 
2-Feb-2014 
2-Feb-2015 

注意事项:

  • lstRes 声明为IEnumerable&lt;string&gt; 可防止不必要地创建将被丢弃的额外列表 每次迭代

  • 使用本能的null,以便第一个交叉连接可以建立一些东西(使用字符串,null + s = s

【讨论】:

  • 非常好的解决方案,没想到。
【解决方案2】:

要使这真正动态化,您需要两个 int 循环变量数组(索引和计数):

int numLoops = lstMaster.Count;
int[] loopIndex = new int[numLoops];
int[] loopCnt = new int[numLoops];

然后你需要遍历所有这些循环索引的逻辑。

初始化到起始值(可选)

for(int i = 0; i < numLoops; i++) loopIndex[i] = 0;
for(int i = 0; i < numLoops; i++) loopCnt[i] = lstMaster[i].Count;

最后是一个适用于所有组合的大循环。

bool finished = false;
while(!finished)
{
     // access current element
     string line = "";
     for(int i = 0; i < numLoops; i++)
     {
         line += lstMaster[i][loopIndex[i]];
     }
     llstRes.Add(line);
     int n = numLoops-1;                  
     for(;;)
     {
         // increment innermost loop
         loopIndex[n]++;
         // if at Cnt: reset, increment outer loop
         if(loopIndex[n] < loopCnt[n]) break;

         loopIndex[n] = 0;
         n--;
         if(n < 0)
         { 
             finished=true;
             break;
         }
     }       
}

【讨论】:

  • 虽然@D-Stanley 的stackoverflow.com/a/28610590/1444246 回答解决了问题,但您建议的回答需要我的要求。非常感谢。
  • 这也是对我有用的解决方案。当使用 D-Stanleys 算法组合复杂对象类型时,我得到了正在组合的子列表的笛卡尔积。而我需要的是对每个可能的组合进行 1 次组合尝试,@DrKochs 流程可以很好地实现。
【解决方案3】:
    public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<IEnumerable<T>> lists)
    {
         IEnumerable<IEnumerable<T>> result = new List<IEnumerable<T>> { new List<T>() };
         return lists.Aggregate(result, (current, list) => current.SelectMany(o => list.Select(s => o.Union(new[] { s }))));
    }

【讨论】:

  • 为了得到一个有用的答案,这个反应需要扩展。解释为什么这是问题的答案。
【解决方案4】:
        var totalCombinations = 1;
        foreach (var l in lstMaster)
        {
            totalCombinations *= l.Count == 0 ? 1 : l.Count;
        }

        var res = new string[totalCombinations];
        for (int i = 0; i < lstMaster.Count; ++i)
        {
            var numOfEntries = totalCombinations / lstMaster[i].Count;
            for (int j = 0; j < lstMaster[i].Count; ++j)
            {
                for (int k = numOfEntries * j; k < numOfEntries * (j + 1); ++k)
                {
                    if (res[k] == null)
                    {
                        res[k] = lstMaster[i][j];
                    }
                    else
                    {
                        res[k] += lstMaster[i][j];
                    }
                }
            }
        }

算法从计算所有子列表需要多少组合开始。

当我们知道我们创建了一个包含这个数量的条目的结果数组时。然后算法遍历所有子列表,从子列表中提取项目并计算该项目应在结果中出现的次数,并将该项目添加到结果中指定的次数。移动到同一列表中的下一个项目并添加到剩余字段(如果列表中有两个以上的项目,则根据需要添加)。它继续遍历所有子列表和所有项目。

需要改进的地方是列表为空时。存在 DivideByZeroException 的风险。我没有添加那个。我更愿意专注于传达计算背后的想法,并且不想通过额外的检查来混淆它。

【讨论】:

    猜你喜欢
    • 2013-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-10
    • 1970-01-01
    相关资源
    最近更新 更多