26

I've a List of this type List> that contains this

List<int> A = new List<int> {1, 2, 3, 4, 5};
List<int> B = new List<int> {0, 1};
List<int> C = new List<int> {6};
List<int> X = new List<int> {....,....};

I want to have all combinations like this

1-0-6
1-1-6
2-0-6
2-1-6
3-0-6

and so on.

According to you is This possibile to resolve using Linq?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Giomuti
  • 301
  • 1
  • 3
  • 7

9 Answers9

39

It's quite similar to this answer I gave to another question:

var combinations = from a in A
                   from b in B
                   from c in C
                   orderby a, b, c
                   select new List<int> { a, b, c };

var x = combinations.ToList();

For a variable number of inputs, now with added generics:

var x = AllCombinationsOf(A, B, C);

public static List<List<T>> AllCombinationsOf<T>(params List<T>[] sets)
{
    // need array bounds checking etc for production
    var combinations = new List<List<T>>();

    // prime the data
    foreach (var value in sets[0])
        combinations.Add(new List<T> { value });

    foreach (var set in sets.Skip(1))
        combinations = AddExtraSet(combinations, set);

    return combinations;
}

private static List<List<T>> AddExtraSet<T>
     (List<List<T>> combinations, List<T> set)
{
    var newCombinations = from value in set
                          from combination in combinations
                          select new List<T>(combination) { value };

    return newCombinations.ToList();
}
Community
  • 1
  • 1
Garry Shutler
  • 32,260
  • 12
  • 84
  • 119
  • 1
    I don't think that works... I believe (from the X) that the OP means that the number of items in the list (and thus the number of dimensions) is dynamic – Marc Gravell Feb 13 '09 at 12:33
  • Hmmm, it's still possible based on my other references answer, you could just take a paramarray of sets and build it up. I'll ask for clarification in a comment. – Garry Shutler Feb 13 '09 at 12:34
  • I see you beat me to doing just that! – Garry Shutler Feb 13 '09 at 12:35
  • Yes guys the number of items is dynamic! – Giomuti Feb 13 '09 at 13:51
  • 1
    I see - you repeatedly cross 2 lists at a time in the loop - cute. – Marc Gravell Feb 13 '09 at 13:56
  • How Can I modify the function "AllCombinationsOf" to pass all the list and only list? – Giomuti Feb 13 '09 at 14:05
  • AllCombinationsOf will take any number of lists (due to the params) argument, so long as those lists contain the same type (in this case int). So you could call AllCombinationsOf(List, List, List, List) without changing any code. – Garry Shutler Feb 13 '09 at 14:12
  • I've tried, it functions. But I need to pass to the function the entire list not only single elements! – Giomuti Feb 13 '09 at 14:13
  • I've understood but i want to pass the entire List> – Giomuti Feb 13 '09 at 14:14
  • I don't understand. You want to pass it the list of combinations in order to create a list of combinations? – Garry Shutler Feb 13 '09 at 14:23
  • Sorry for my english, I'm Italian. I' ve solved. This example maybe could help you! var A = new List() { 1, 2, 3, 4, 5 }; var B = new List() { 1, 3, 5 }; List> L = new List>(); L.Add(A); L.Add(B); var X = Combinations.AllCombinationsOf(L.ToArray()); Thanks very much. – Giomuti Feb 13 '09 at 14:28
  • If that's how you need to use it you could change the method to this signature: AllCombinationsOf(List> sets) I think it would still work without any other changes to the code. Otherwise what you've done will work, yes. – Garry Shutler Feb 13 '09 at 14:31
  • The biggest problem I can see with this approach is the number of allocations. With the array version, you don't have this - just the one array. It would be easy to pass in an Action to enact on combinations... – Marc Gravell Feb 13 '09 at 21:45
  • It is true, I was thinking about it last night. I'll have a think about it. However, it's currently readable (to me) and produces the correct result. So (potentially) obfusticating it in the name of performance may be a waste of time if it works sufficiently well. – Garry Shutler Feb 14 '09 at 10:11
  • Hi, I need to verify, during combination, if a value is greater than other. What do I have to do? Thanks very much!!! – Giomuti Feb 16 '09 at 12:38
15

If the number of dimensions is fixed, this is simply SelectMany:

var qry = from a in A
          from b in B
          from c in C
          select new {A=a,B=b,C=c};

However, if the number of dimensions is controlled by the data, you need to use recursion:

static void Main() {
    List<List<int>> outerList = new List<List<int>>
    {   new List<int>(){1, 2, 3, 4, 5},
        new List<int>(){0, 1},
        new List<int>(){6,3},
        new List<int>(){1,3,5}
    };
    int[] result = new int[outerList.Count];
    Recurse(result, 0, outerList);
}
static void Recurse<TList>(int[] selected, int index,
    IEnumerable<TList> remaining) where TList : IEnumerable<int> {
    IEnumerable<int> nextList = remaining.FirstOrDefault();
    if (nextList == null) {
        StringBuilder sb = new StringBuilder();
        foreach (int i in selected) {
            sb.Append(i).Append(',');
        }
        if (sb.Length > 0) sb.Length--;
        Console.WriteLine(sb);
    } else {
        foreach (int i in nextList) {
            selected[index] = i;
            Recurse(selected, index + 1, remaining.Skip(1));
        }
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
9

How about following way of generating combinations using .Join method?

static void Main()
{
    List<List<int>> collectionOfSeries = new List<List<int>>
                                {   new List<int>(){1, 2, 3, 4, 5},
                                    new List<int>(){0, 1},
                                    new List<int>(){6,3},
                                    new List<int>(){1,3,5}
                                };
    int[] result = new int[collectionOfSeries.Count];

    List<List<int>> combinations = GenerateCombinations(collectionOfSeries);

    Display(combinations); 
}

This Method GenerateCombinations(..) does main work of generating combinations. This method is generic so could be used for generating combinations of any type.

private static List<List<T>> GenerateCombinations<T>(
                                List<List<T>> collectionOfSeries)
{
    List<List<T>> generatedCombinations = 
        collectionOfSeries.Take(1)
                          .FirstOrDefault()
                          .Select(i => (new T[]{i}).ToList())                          
                          .ToList();

    foreach (List<T> series in collectionOfSeries.Skip(1))
    {
        generatedCombinations = 
            generatedCombinations
                  .Join(series as List<T>,
                        combination => true,
                        i => true,
                        (combination, i) =>
                            {
                                List<T> nextLevelCombination = 
                                    new List<T>(combination);
                                nextLevelCombination.Add(i);
                                return nextLevelCombination;
                            }).ToList();

    }

    return generatedCombinations;
}

Display helper..

private static void Display<T>(List<List<T>> generatedCombinations)
{
    int index = 0;
    foreach (var generatedCombination in generatedCombinations)
    {
        Console.Write("{0}\t:", ++index);
        foreach (var i in generatedCombination)
        {
            Console.Write("{0,3}", i);
        }
        Console.WriteLine();
    }
    Console.ReadKey();
}
John Conde
  • 217,595
  • 99
  • 455
  • 496
Abhijeet Nagre
  • 876
  • 1
  • 11
  • 21
2
//Done in 2 while loops. No recursion required
#include<stdio.h>
#define MAX 100
typedef struct list
{
  int elements[MAX];
}list;
list n[10];
int number,count[10],temp[10];
void print();
int main()
{
  int i,j,mult=1,mult_count;
  printf("Enter the number of lists - ");
  scanf("%d",&number);
  for(i=0;i<number;i++)
  {
    printf("Enter the number of elements - ");
    scanf("%d",&count[i]);
    for(j=0;i<count[i];j++)
    {
      printf("Enter element %d - "j);
      scanf("%d",&n[i].elements[j]);
    }
  }
  for(i=0;i<number;i++)
  temp[i]=0;
  for(i=0;i<number;i++)
  mult*=count[i];
  printf("%d\n",mult);
  mult_count=0;
  while(1)
  {
    print();
    mult_count++;
    if(mult_count==mult)
    break;
    i=0;
    while(1)
    {
      temp[i]++;
      if(temp[i]==count[i])
      {
        temp[i]=0;
        i++;
      }
      else break;
    }
  }
  return 0;
}
void print()
{
  int i;
  for(i=0;i<number;i++)
  {
    printf("%d\n",n[i].elements[temp[i]]);
    printf("\n");
  }
}
2

Great solution from Abhijeet Nagre. Small improvement in case when some serie is empty or series are empty.

List<List<T>> generatedCombinations = 
    collectionOfSeries.Where(l => l.Any())
                      .Take(1)
                      .DefaultIfEmpty(new List<T>())
                      .First()
                      .Select(i => (new T[]{i}).ToList())                          
                      .ToList();
Waclaw
  • 21
  • 1
  • 3
1

Just for fun:

using CSScriptLibrary;
using System;
using System.Collections.Generic;

namespace LinqStringTest
{
    public class Program
    {
        static void Main(string[] args)
        {

            var lists = new List<List<int>>() {
                new List<int> { 0, 1, 2, 3 },
                new List<int> { 4, 5 },
                new List<int> { 6, 7 },
                new List<int> { 10,11,12 },
            };
            var code = GetCode(lists);
            AsmHelper scriptAsm = new AsmHelper(CSScript.LoadCode(code));

            var result = (IEnumerable<dynamic>)scriptAsm.Invoke("Script.LinqCombine", lists);

            foreach (var item in result)
            {
                Console.WriteLine(item);
            }

            Console.ReadLine();
        }

        private static string GetCode(List<List<int>> listsToCombine)
        {
            var froms = "";
            var selects = "";

            for (int i = 0; i < listsToCombine.Count; i++)
            {
                froms += string.Format("from d{0} in lists[{0}]{1}", i, Environment.NewLine);
                selects += string.Format("D{0} = d{0},", i);
            }

            return @"using System;
              using System.Linq;
              using System.Collections.Generic;
              public class Script
              {
                  public static IEnumerable<dynamic> LinqCombine(List<List<int>> lists)
                  {
                        var x = " + froms + @"
                                select new { " + selects + @" };
                        return x;
                  }
              }";
        }
    }
}
Jbjstam
  • 874
  • 6
  • 13
1
    public static List<List<string>> CrossProduct(List<List<string>> s)
    {
        if (!s.Any())
            return new List<List<string>>();

        var c1 = s.First();
        var cRest = s.Skip(1).ToList();
        var sss = from v1 in c1
                  from vRest in CrossProduct(cRest)
                  select (new[] { v1 }.Concat(vRest)).ToList();
        var r = sss.ToList();
        return r;
    }
DenNukem
  • 8,014
  • 3
  • 40
  • 45
1

Solution without Linq and recursion:

private List<List<T>> GetAllCombinations<T>(List<List<T>> source)
{
    List<List<T>> result = new List<List<T>>();

    foreach (var value in source[0])
    {
        result.Add(new List<T> { value });
    }

    for (int i = 1; i < source.Count; i++)
    {
        var resultCount = result.Count;

        for (int j = 1; j < source[i].Count; j++)
        {
            for (var k = 0; k < resultCount; k++)
            {
                result.Add(new List<T>(result[k]));
            }
        }

        var t = (result.Count / source[i].Count);

        for (int j = 0; j < source[i].Count; j++)
        {
            for (int k = 0; k < t; k++)
            {
                result[j * t + k].Add(source[i][j]);
            }
        }
    }

    return result;
}
0

Generates all combinations from the input lists

var combinations = AllCombinationsOf(A, B, C);

public static IEnumerable<List<T>> AllCombinationsOf<T>(params List<T>[] inputs)
{
    var seed = Enumerable.Repeat(new List<T>(), 1);
    return inputs.Aggregate(seed, CreateCombinations);
}

private static IEnumerable<List<T>> CreateCombinations<T>(IEnumerable<List<T>> oldCombinations, List<T> newValues)
    =>  from value in newValues
        from combination in oldCombinations
        select new List<T>(combination) {value};

Simplified version of this answer by @Garry Shutler.

Try it online! (test cases included along with the question's example)

NonlinearFruit
  • 2,509
  • 1
  • 25
  • 27