I have an ArrayList[] myList and I am trying to create a list of all the permutations of the values in the arrays.
EXAMPLE: (all values are strings)
Here is a generic recursive function that I wrote (and an overload that may be convenient to call):
Public Shared Function GetCombinationsFromIEnumerables(ByRef chain() As Object, ByRef IEnumerables As IEnumerable(Of IEnumerable(Of Object))) As List(Of Object())
Dim Combinations As New List(Of Object())
If IEnumerables.Any Then
For Each v In IEnumerables.First
Combinations.AddRange(GetCombinationsFromIEnumerables(chain.Concat(New Object() {v}).ToArray, IEnumerables.Skip(1)).ToArray)
Next
Else
Combinations.Add(chain)
End If
Return Combinations
End Function
Public Shared Function GetCombinationsFromIEnumerables(ByVal ParamArray IEnumerables() As IEnumerable(Of Object)) As List(Of Object())
Return GetCombinationsFromIEnumerables(chain:=New Object() {}, IEnumerables:=IEnumerables.AsEnumerable)
End Function
And the equivalent in C#:
public static List<object[]> GetCombinationsFromIEnumerables(ref object[] chain, ref IEnumerable<IEnumerable<object>> IEnumerables)
{
List<object[]> Combinations = new List<object[]>();
if (IEnumerables.Any) {
foreach ( v in IEnumerables.First) {
Combinations.AddRange(GetCombinationsFromIEnumerables(chain.Concat(new object[] { v }).ToArray, IEnumerables.Skip(1)).ToArray);
}
} else {
Combinations.Add(chain);
}
return Combinations;
}
public static List<object[]> GetCombinationsFromIEnumerables(params IEnumerable<object>[] IEnumerables)
{
return GetCombinationsFromIEnumerables(chain = new object[], IEnumerables = IEnumerables.AsEnumerable);
}
Easy to use:
Dim list1 = New String() {"hello", "bonjour", "hallo", "hola"}
Dim list2 = New String() {"Erwin", "Larry", "Bill"}
Dim list3 = New String() {"!", ".."}
Dim result = MyLib.GetCombinationsFromIEnumerables(list1, list2, list3)
For Each r In result
Debug.Print(String.Join(" "c, r))
Next
or in C#:
object list1 = new string[] {"hello","bonjour","hallo","hola"};
object list2 = new string[] {"Erwin", "Larry", "Bill"};
object list3 = new string[] {"!",".."};
object result = MyLib.GetCombinationsFromIEnumerables(list1, list2, list3);
foreach (r in result) {
Debug.Print(string.Join(' ', r));
}
Non-recursive solution:
foreach (String s1 in array1) {
foreach (String s2 in array2) {
foreach (String s3 in array3) {
String result = s1 + " " + s2 + " " + s3;
//do something with the result
}
}
}
Recursive solution:
private ArrayList<String> permute(ArrayList<ArrayList<String>> ar, int startIndex) {
if (ar.Count == 1) {
foreach(String s in ar.Value(0)) {
ar.Value(0) = "val" + startIndex + "=" + ar.Value(0);
return ar.Value(0);
}
ArrayList<String> ret = new ArrayList<String>();
ArrayList<String> tmp1 ar.Value(0);
ar.remove(0);
ArrayList<String> tmp2 = permute(ar, startIndex+1);
foreach (String s in tmp1) {
foreach (String s2 in tmp2) {
ret.Add("val" + startIndex + "=" + s + " " + s2);
}
}
return ret;
}
class Program
{
static void Main(string[] args)
{
var listofInts = new List<List<int>>(3);
listofInts.Add(new List<int>{1, 2, 3});
listofInts.Add(new List<int> { 4,5,6 });
listofInts.Add(new List<int> { 7,8,9,10 });
var temp = CrossJoinLists(listofInts);
foreach (var l in temp)
{
foreach (var i in l)
Console.Write(i + ",");
Console.WriteLine();
}
}
private static IEnumerable<List<T>> CrossJoinLists<T>(IEnumerable<List<T>> listofObjects)
{
var result = from obj in listofObjects.First()
select new List<T> {obj};
for (var i = 1; i < listofObjects.Count(); i++)
{
var iLocal = i;
result = from obj in result
from obj2 in listofObjects.ElementAt(iLocal)
select new List<T>(obj){ obj2 };
}
return result;
}
}
Here is a version which uses very little code, and is entirely declarative
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> collection) where T : IComparable
{
if (!collection.Any())
{
return new List<IEnumerable<T>>() {Enumerable.Empty<T>() };
}
var sequence = collection.OrderBy(s => s).ToArray();
return sequence.SelectMany(s => GetPermutations(sequence.Where(s2 => !s2.Equals(s))).Select(sq => (new T[] {s}).Concat(sq)));
}
Here's a non-recursive, non-Linq solution. I can't help feeling like I could have less looping and calculate the positions with division and modulo, but can't quite wrap my head around that.
static void Main(string[] args)
{
//build test list
List<string[]> myList = new List<string[]>();
myList.Add(new string[0]);
myList.Add(new string[0]);
myList.Add(new string[0]);
myList[0] = new string[] { "1", "2", "3"};
myList[1] = new string[] { "4", "5" };
myList[2] = new string[] { "7", "8", "9" };
object[][] xProds = GetProducts(myList.ToArray());
foreach(object[] os in xProds)
{
foreach(object o in os)
{
Console.Write(o.ToString() + " ");
}
Console.WriteLine();
}
Console.ReadKey();
}
static object[][] GetProducts(object[][] jaggedArray){
int numLists = jaggedArray.Length;
int nProducts = 1;
foreach (object[] oArray in jaggedArray)
{
nProducts *= oArray.Length;
}
object[][] productAry = new object[nProducts][];//holds the results
int[] listIdxArray = new int[numLists];
listIdxArray.Initialize();
int listPtr = 0;//point to current list
for(int rowcounter = 0; rowcounter < nProducts; rowcounter++)
{
//create a result row
object[] prodRow = new object[numLists];
//get values for each column
for(int i=0;i<numLists;i++)
{
prodRow[i] = jaggedArray[i][listIdxArray[i]];
}
productAry[rowcounter] = prodRow;
//move the list pointer
//possible states
// 1) in a list, has room to move down
// 2) at bottom of list, can move to next list
// 3) at bottom of list, no more lists left
//in a list, can move down
if (listIdxArray[listPtr] < (jaggedArray[listPtr].Length - 1))
{
listIdxArray[listPtr]++;
}
else
{
//can move to next column?
//move the pointer over until we find a list, or run out of room
while (listPtr < numLists && listIdxArray[listPtr] >= (jaggedArray[listPtr].Length - 1))
{
listPtr++;
}
if (listPtr < listIdxArray.Length && listIdxArray[listPtr] < (jaggedArray[listPtr].Length - 1))
{
//zero out the previous stuff
for (int k = 0; k < listPtr; k++)
{
listIdxArray[k] = 0;
}
listIdxArray[listPtr]++;
listPtr = 0;
}
}
}
return productAry;
}
What you are asking for is called the Cartesian Product. Once you know what its called, there are several similar questions on Stack Overflow. They all seem to end up pointing to an answer which ended up written as a blog post:
http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx