If you want to get, say, all combinations of 3
items, you can keep array indexes
where you can put which items to take:
uniqueWords = {'A', 'B', 'C', 'D'}
indexes = {0, 0, 0} // AAA
indexes = {0, 0, 1} // AAB
indexes = {0, 0, 2} // AAC
indexes = {0, 1, 0} // ABA
...
indexes = {2, 2, 2} // CCC
Code: (let's implement it in general case, with IEnumerable<T>
as an input argument)
using System.Linq;
...
private static IEnumerable<T[]> AllWords<T>(IEnumerable<T> uniqueWords, int count) {
T[] words = uniqueWords.ToArray();
int[] indexes = new int[count];
do {
yield return indexes.Select(index => words[index]).ToArray();
for (int i = indexes.Length - 1; i >= 0; --i)
if (++indexes[i] >= words.Length)
indexes[i] = 0;
else
break;
}
while (!indexes.All(index => index == 0));
}
Demo:
List<char> demo = new List<char>() { 'A', 'B', 'C', 'D' };
int count = 2;
var result = string.Join(Environment.NewLine, AllWords(demo, count)
.Select(array => string.Join(" ", array)));
Console.Write(result);
Output:
A A
A B
A C
A D
B A
B B
B C
B D
C A
C B
C C
C D
D A
D B
D C
D D
Edit: If want all generated words to be unique, i.e. you want to exclude A A
, B B
etc. you can either add Where
:
List<char> demo = new List<char>() { 'A', 'B', 'C', 'D' };
int count = 2;
var result = string.Join(Environment.NewLine, AllWords(demo, count)
.Where(array => array.Distinct().Count() == count)
.Select(array => string.Join(" ", array)));
Console.Write(result);
Or modify slightly modify AllWords
:
private static IEnumerable<T[]> AllWords<T>(IEnumerable<T> uniqueWords, int count) {
T[] words = uniqueWords.ToArray();
int[] indexes = new int[count];
HashSet<int> unique = new();
do {
unique.Clear();
foreach (int item in indexes)
unique.Add(item);
if (unique.Count == indexes.Length)
yield return indexes.Select(index => words[index]).ToArray();
for (int i = indexes.Length - 1; i >= 0; --i)
if (++indexes[i] >= words.Length)
indexes[i] = 0;
else
break;
}
while (!indexes.All(index => index == 0));
}
Output:
A B
A C
A D
B A
B C
B D
C A
C B
C D
D A
D B
D C