用于列出对象的唯一组合的嵌套循环的动态数量
本文关键字:嵌套循环 动态 组合 唯一 对象 用于 | 更新日期: 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