嵌套for循环的动态数量,用于列出对象的唯一组合

Nit*_*ion 5 c# arrays recursion dynamic object

我有n个对象列表,我需要转换为一个对象数组列表,每个对象数组都包含原始列表中唯一的对象组合.

例:

myList[0] = new List<object>(){a, b, c, d};
myList[1] = new List<object>(){"0", "1", "2", "3", "4"};
myList[2] = new List<object>(){0, 1, 2};
myList[3] = new List<object>(){aClass, bClass}
Run Code Online (Sandbox Code Playgroud)

等等

需要成为:

newList[0] =  new object[]{a, "0", 0, aClass};
newList[1] =  new object[]{a, "0", 0, bClass};
newList[2] =  new object[]{a, "0", 1, aClass};
newList[3] =  new object[]{a, "0", 1, bClass};
newList[4] =  new object[]{a, "0", 2, aClass};
newList[5] =  new object[]{a, "0", 2, bClass};
newList[6] =  new object[]{a, "1", 0, aClass};
newList[7] =  new object[]{a, "1", 0, bClass};
newList[8] =  new object[]{a, "1", 1, aClass};
newList[9] =  new object[]{a, "1", 1, bClass};
newList[10] = new object[]{a, "1", 2, aClass};
newList[11] = new object[]{a, "1", 2, bClass};
Run Code Online (Sandbox Code Playgroud)

等等

必须保留变量的顺序(myList [0]中的列表必须是第一个,等等),因为这些对象数组是通过反射传递的参数:

Indicator temp = (Indicator) newIndicator.Invoke(this, newList[i]);
Run Code Online (Sandbox Code Playgroud)

如果对象列表的数量是静态的,则可能如下所示:

List<object[]> newList = new List<object[]>();
for(int i = 0; i < myList[0].Count; i++)
{
    for(int i2 = 0; i2 < myList[1].Count; i2++)
    {
        for(int i3 = 0; i3 < myList[2].Count; i3++)
        {
            for(int i4 = 0; i4 < myList[3].Count; i4++)
            {
                object[] temp = new object[]{myList[0][i], myList[1][i2], myList[2][i3], myList[3][i4]};
                newList.Add(temp);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我最近的尝试是创建一个标记列表,其中包含每个列表的当前索引并适当增加它,但是我的数学似乎没有成功,因为我将其扩展.

        private List<object[]> ToParametersList(List<List<object>> listOfLists)
    {
        int counter = 1;
        foreach(List<object> list in listOfLists){ counter *= list.Count; }

        List<object[]> returnList = new List<object[]>();

        List<int> indicies = new List<int>();
        int tempSplit = 0;
        List<int> splits = new List<int>();
        List<int> splitcounters = new List<int>();
        for(int i = 0; i < listOfLists.Count; i++)
        {
            if(i == 0 && listOfLists[0].Count > 2)
            {
                splits.Add(counter / listOfLists[0].Count);
                tempSplit = counter / listOfLists[0].Count;
            } else if(i > 0 && listOfLists[i].Count > 2) {
                splits.Add(tempSplit / listOfLists[i].Count);
                tempSplit /= listOfLists[i].Count;
            } else if(listOfLists[i].Count == 2)
            {
                splits.Add(1);
            }
            indicies.Add(0);
            splitcounters.Add(1);
        }
        for(int i = 0; i < counter; i++)
        {
            object[] newObject = new object[listOfLists.Count];
            for(int i2 = 0; i2 < listOfLists.Count; i2++)
            {
                if(i < splits[i2] * splitcounters[i2] && ((indicies[i2] < listOfLists[i2].Count && listOfLists[i2].Count > 2) || indicies[i2] < listOfLists[i2].Count - 1))
                {
                    newObject[i2] = listOfLists[i2][indicies[i2]];
                }
                else if(i >= splits[i2] * splitcounters[i2] && ((indicies[i2] < listOfLists[i2].Count && listOfLists[i2].Count > 2) || indicies[i2] < listOfLists[i2].Count - 1))
                {
                    indicies[i2]++;
                    splitcounters[i2]++;
                    newObject[i2] = listOfLists[i2][indicies[i2]];
                }
                else
                {
                    indicies[i2] = 0;
                    splitcounters[i2]++;
                    newObject[i2] = listOfLists[i2][indicies[i2]];
                }
            }
            returnList.Add(newObject);
        }
        return returnList;
    }
Run Code Online (Sandbox Code Playgroud)

我也在这里经历了许多递归问题,并且仍然无法理解如何将它们应用于这种特殊情况(我对递归相对较新).

任何帮助将不胜感激!

编辑:在具有n个列表笛卡尔产品中,OP的帖子令人困惑,并且提供的答案没有解释正在发生的事情.链接到Eric Lippert的博客是笛卡尔产品的一般概述,它没有帮助我打破我需要在我试图做的事情中正确理解这一点的障碍.

M.k*_*ary 1

老实说,我没有读过你上次的尝试。使用 Linq 的其他方法也很棒,但如果您确实想要递归,请遵循这种方法。

要创建良好的递归,您需要查看方法的哪些部分有所不同,哪些部分没有。该方法应该采用每次调用时都不同的参数。此外,您还需要 if-else 来在某处结束递归。

List<object[]> newList = new List<object[]>();
for(int i = 0; i < myList[0].Count; i++)
{
    for(int i2 = 0; i2 < myList[1].Count; i2++)
    {
        for(int i3 = 0; i3 < myList[2].Count; i3++)
        {
            for(int i4 = 0; i4 < myList[3].Count; i4++)
            {
                object[] temp = new object[]{myList[0][i], myList[1][i2], myList[2][i3], myList[3][i4]};
                newList.Add(temp);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我们希望在此方法中使用递归,以便能够将其用于任意长度的列表。为此,您必须将循环转换为递归调用。但现在你有未知数量的循环。

解决方案是使用params关键字。您可以发送任意数量的int到方法。这int保存了变量i1, i2 , i3 , i4 ...。就像你写的上面的方法一样。

该数组 ( ) 的长度params int[]恰好是普通方法内的循环数。

private static void Combine(List<List<object>> myList,List<object[]> newList,params int[] loopInd)
{
    if (loopInd.Length <= myList.Count) // should not exceed number of loops.
    {
        int currentCount = myList[loopInd.Length - 1].Count;

        while (loopInd[loopInd.Length - 1] < currentCount) // i<myList[0] , i2<myList[1] , i3<myList[2]
        {
            Combine(myList, newList, loopInd.Concat(new[] {0}).ToArray()); // Go for inner loop
            loopInd[loopInd.Length - 1]++; // i++, i2++ , i3++ ...
        }
    }
    else // no more loops.add the object[] into newList
    {
        int j = 0;
        object[] temp = loopInd.Take(loopInd.Length - 1).Select(i => myList[j++][i]).ToArray();
        newList.Add(temp);
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的注释显示了正常方法中的表示。

那么就可以按照这样的方式来使用了。

List<List<object>> myList = new List<List<object>>();
myList.Add(new List<object>() { a, b, c, d });
myList.Add(new List<object>() { "0", "1", "2", "3", "4" });
myList.Add(new List<object>() { 0, 1, 2 });
myList.Add(new List<object>() {aClass, bClass});

List<object[]> newList = new List<object[]>();

Combine(myList, newList, 0);

        // The newList is now what you want
Run Code Online (Sandbox Code Playgroud)

编辑 :

如果您追求性能,您可以转换此 Linq 部分

int j = 0;
object[] temp = loopInd.Take(loopInd.Length - 1).Select(i => myList[j++][i]).ToArray();
newList.Add(temp);
Run Code Online (Sandbox Code Playgroud)

进入代码

int j = 0;
object[] temp = new object[loopInd.Length - 1];

for (int i = 0; i < loopInd.Length - 1; i++,j++) 
{
    temp[i] = myList[j][loopInd[i]];
}
Run Code Online (Sandbox Code Playgroud)