7

I have a set of strings, and I want to find all possible combinations of the strings and add them to a list. I want to end up with a list of a list of each combination of the strings, minus the empty set.

I have created a solution that does this exactly with a nested for loop. However I want to do this more elegantly, preferably with LINQ, and I am not so proficient with it because I'm still pretty new to it.

The solution should have 2^n - 1 lists of combinations where n is the cardinality of the original set. Here is a correct example of what I am looking for:

set = {a, b, c}

completedListOfCombinations = 
{
    {a},
    {b},
    {a, b},
    {c},
    {a, c},
    {b, c},
    {a, b, c}
}

Here is my working, basic but ugly solution that I crafted with help from: https://stackoverflow.com/a/3319652/3371287

List<string> myStrings =  new List<string> { "a", "b", "c" };

var allCombos = new List<List<string>>();

for (int i = 0; i < myStrings.Count; i++)
{
    int subsetCount = allCombos.Count;
    var m = new List<string>();
    m.Add(myStrings[i]);
    allCombos.Add(m);

    for (int j = 0; j < subsetCount; j++)
    {
        string[] subset = new string[allCombos.ElementAt(j).Count + 1];
        allCombos[j].CopyTo(subset, 0);
        subset[subset.Length - 1] = myStrings[i];
        allCombos.Add(subset.ToList());
    }

}

Can someone show me a more elegant solution for this? I have seen similar LINQ solutions that create Cartesian Pairs and lists with a threshold, but I have not been able to tweak them to what I need.

Community
  • 1
  • 1
user3371287
  • 93
  • 1
  • 6
  • This may be more appropriate over on [Code Review](http://codereview.stackexchange.com) – James Thorpe May 06 '15 at 16:08
  • possible duplicate of [All combinations of items in N arrays in C#](http://stackoverflow.com/questions/13616545/all-combinations-of-items-in-n-arrays-in-c-sharp) – mbeckish May 06 '15 at 16:17

1 Answers1

16

Providing that all the values in the list are unique:

  List <String> list = new List<String> { "a", "b", "c" };

  var result = Enumerable
    .Range(1, (1 << list.Count) - 1)
    .Select(index => list.Where((item, idx) => ((1 << idx) & index) != 0).ToList());

To print out:

Console.WriteLine(String
  .Join(Environment.NewLine, result
     .Select(line => String.Join(", ", line))));

The outcome is

a
b
a, b
c
a, c
b, c
a, b, c
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • Yes, the values in the list are always unique. Well done! I want to dissect and understand this. – user3371287 May 06 '15 at 16:28
  • 5
    You can think of it as counting from 1 to 2^n-1. If you look at the index in binary (001 -> 010 -> 011 -> 100 -> 101 -> 110 -> 111) then map each binary digit to one of your list values ("a","b","c"), and only show the ones that are 1 (I'll show 0's as 0 instead of not showing them at all), then you get (00a, 0b0, 0ba, c00, c0a, cb0, cba) – Robert McKee May 06 '15 at 17:02