I need to create a list from another list that contains every combination possible. In researching possible solutions I have found many interesting approaches but all seem to generate results based on the count of records provided. I need the combinations to increase to a max threshold.
i.e. consider the following array
1,2,3,4,5
I need the results to look similar to (threshold is 3 in this example)
1
1,2
1,2,3
1,2,4
1,2,5
1,3,4
2,3,5... etc
In actuality, the data will be IEnumerable. I used a simple int[] to illustrate the desired results.
My solution uses a simple recursive algorithm to create the combinations:
When we walk through the sequence we can immediately return a sequence that only holds the current value. I have written a simple extension method to create an IEnumerable for a single item.
Next we recursively generate all combinations for the remaining elements with the threshold reduced by 1 and combine each of them with the current value.
I assume that elements should not be repeated (i.e. { 1, 1 } or { 1, 2, 1 } are not allowed). If you want to allow repeated elements you can remove the remaining
variable and replace it with values
in the recursive call to GetCombinations
.
Please note the use of the yield
keyword. This means that the code uses deferred execution. There is no need to store any intermediate results before you actually enumerate the result.
public static IEnumerable<IEnumerable<T>> GetCombinations<T>(IEnumerable<T> values, int threshold)
{
var remaining = values;
foreach (T value in values)
{
yield return value.Yield();
if (threshold < 2)
{
continue;
}
remaining = remaining.Skip(1);
foreach (var combination in GetCombinations(remaining, threshold - 1))
{
yield return value.Yield().Concat(combination);
}
}
}
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
For the integer array { 1, 2, 3, 4, 5 } the output is:
1
1, 2
1, 2, 3
1, 2, 4
1, 2, 5
1, 3
1, 3, 4
1, 3, 5
1, 4
1, 4, 5
1, 5
2
2, 3
2, 3, 4
2, 3, 5
2, 4
2, 4, 5
2, 5
3
3, 4
3, 4, 5
3, 5
4
4, 5
5
Assuming you already have a solution to find the combinations for a particular count (which you've said you have), let's say of the signature:
public static IEnumerable<IEnumerable<T>> Combinations<T>(
IList<T> source, int count)
Then you can easily get the combinations for all counts by calling it N times:
public static IEnumerable<IEnumerable<T>> Combinations<T>(IList<T> source)
{
return Enumerable.Range(0, source.Count)
.SelectMany(i => Combinations(source, i));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With