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

本文关键字:嵌套循环 动态 组合 唯一 对象 用于 | 更新日期: 2023-09-27 18:13:43

我有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}

等。

需要变成:

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

等。

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

Indicator temp = (Indicator) newIndicator.Invoke(this, newList[i]);

如果对象列表的数量是静态的,它可能看起来像下面这样:

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

我最近的尝试是创建一个索引列表,其中包含每个列表的当前索引并适当地增加它,但是我的数学似乎不工作,因为我扩展它。

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

我也在这里经历了许多递归问题,但仍然难以理解如何将它们应用于这种特殊情况(我对递归比较陌生)。

任何帮助都将非常感激!

编辑:在n个数的笛卡尔产品列表中,OP的帖子令人困惑,提供的答案没有解释发生了什么。Eric Lippert博客的链接是对笛卡尔理论的总体概述,但它并没有帮助我打破障碍,我需要在我试图做的事情的背景下正确理解这一点。

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

老实说,我没有读你的最后一次尝试。其他使用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);
            }
        }
    }
}

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

解决方案是使用params关键字。你可以发送任意数量的int到method。这个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);
    }
}

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

那么你可以这样使用它。

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

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

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

为代码

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

这是一个在编译时接受未知列表的解决方案。
方法CombineArrayOfLists做你需要的:

    static List<List<object>> CombineArrayOfLists(List<object>[] myList)
    {
        List<List<object>> result = myList[0].Select(element => new List<object>() { element }).ToList();
        for (int i = 1; i < myList.Length; i++)
        {
            result = (from c1 in result from c2 in myList[i] select new List<object>(c1) {c2}).ToList();
        }
        return result;
    }

注意,如果列表数组中的任何列表为空,则需要定义所需的行为。要处理这种情况,您可能需要添加一个if语句来跳过该列表(如果这样做是适当的)。

用稍微冗长的形式编写的完整示例,可以更容易理解:

class Program
{
    static void Main(string[] args)
    {
        List<object>[] myList = new List<object>[4];
        AClass aClass = new AClass();
        BClass bClass = new BClass();
        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 };
        List<List<object>> result = CombineArrayOfLists(myList);
        PrintList(result);
    }
    static List<List<object>> CombineArrayOfLists(List<object>[] myList)
    {
        List<List<object>> result = myList[0].Select(element => new List<object>() { element }).ToList();
        for (int i = 1; i < myList.Length; i++)
        {
            result = CombineCollections(result, myList[i]).ToList();
        }
        return result;
    }
    private static IEnumerable<List<object>> CombineCollections(IEnumerable<List<object>> collection1, List<object> collection2)
    {
        return from c1 in collection1 from c2 in collection2 select new List<object>(c1) { c2 };
    }
    // A more verbose form of CombineCollections that may be easier to understand:
    //private static IEnumerable<List<object>> CombineCollections(IEnumerable<List<object>> collection1, List<object> collection2)
    //{
    //    foreach (List<object> c1 in collection1)
    //    {
    //        foreach (object c2 in collection2)
    //        {
    //            List<object> l1 = new List<object>(c1) { c2 };
    //            yield return l1;
    //        }
    //    }
    //}
    private static void PrintList(List<List<object>> collection)
    {
        collection.ForEach(list =>
        {
            list.ForEach(element =>
            {
                Console.Write(element);
                Console.Write(" ");
            });
            Console.WriteLine();
        });
    }
}
public class AClass
{ }
public class BClass
{ }

您可以使用LINQ来获取对象列表的笛卡尔积

List<object> arr1 = new List<object> { "a", "b", "c" };
            List<object> arr2 = new List<object> { 3, 2, 4,5 };
            List<object> arr3 = new List<object> { "0", "1", "2", "3", "4" };

            var result = from x in arr1
                         from y in arr2
                         from z in arr3
                         select new { x = x, y = y,z=z };
            List<object[]> newList = new List<object[]>();
            foreach (var line in result)
            {
                newList.Add(new object[] { line.x, line.y, line.z });                     
            }
            foreach (var obj in newList)
            {
                foreach (var ele in obj)
                {
                    Console.Write(ele.ToString() + " ");
                }
                Console.WriteLine();
            }
            Console.ReadKey();

这将以您需要的方式为您提供一个对象的列表。

下面是正在运行的示例http://csharppad.com/gist/45ebe7c9576dab9c00b8