生成多个字符串列表中的元素组合

时间:2015-02-19 15:23:12

标签: c# .net

我正在尝试自动化嵌套的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);
                            }
                        }
                    }
                }
            }

我想自动化下面的for循环,无论主列表持有的列表项数目是多少?lstMaster

4 个答案:

答案 0 :(得分:5)

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

 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<string>可防止不必要地创建将被丢弃的其他列表   每次迭代

  •   
  • 使用了本能null,以便第一个交叉联接可以构建一些内容(使用字符串,null + s = s

  •   

答案 1 :(得分:2)

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

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

然后你需要逻辑来遍历所有这些loopIndexes。

初始化值(可选)

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;
         }
     }       
}

答案 2 :(得分:0)

        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存在风险。我没有添加。我更愿意专注于传达计算背后的想法,并且不想通过额外的检查来混淆它。

答案 3 :(得分:0)

    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 }))));
    }