How to get every possible combination base on the ranges in brackets? - c#

Looking for the best way to take something like 1[a-C]3[1-6]07[R,E-G] and have it output a log that would look like the following — basically every possible combination base on the ranges in brackets.
1a3107R
1a3107E
1a3107F
1a3107G
1b3107R
1b3107E
1b3107F
1b3107G
1c3107R
1c3107E
1c3107F
1c3107G
all the way to 1C3607G.
Sorry for not being more technical about what I looking for, just not sure on the correct terms to explain.

Normally what we'd do to get all combinations is to put all our ranges into arrays, then use nested loops to loop through each array, and create a new item in the inner loop that gets added to our results.
But in order to do that here, we'd first need to write a method that can parse your range string and return a list of char values defined by the range. I've written a rudimentary one here, which works with your sample input but should have some validation added to ensure the input string is in the proper format:
public static List<char> GetRange(string input)
{
input = input.Replace("[", "").Replace("]", "");
var parts = input.Split(',');
var range = new List<char>();
foreach (var part in parts)
{
var ends = part.Split('-');
if (ends.Length == 1)
{
range.Add(ends[0][0]);
}
else if (char.IsDigit(ends[0][0]))
{
var start = Convert.ToInt32(ends[0][0]);
var end = Convert.ToInt32(ends[1][0]);
var count = end - start + 1;
range.AddRange(Enumerable.Range(start, count).Select(c => (char) c));
}
else
{
var start = (int) ends[0][0];
var last = (int) ends[1][0];
var end = last < start ? 'z' : last;
range.AddRange(Enumerable.Range(start, end - start + 1)
.Select(c => (char) c));
if (last < start)
{
range.AddRange(Enumerable.Range('A', last - 'A' + 1)
.Select(c => (char) c));
}
}
}
return range;
}
Now that we can get a range of values from a string like "[a-C]", we need a way to create nested loops for each range, and to build our list of values based on the input string.
One way to do this is to replace our input string with one that contains placeholders for each range, and then we can create a loop for each range, and on each iteration we can replace the placeholder for that range with a character from the range.
So we'll take an input like this: "1[a-C]3[1-6]07[R,E-G]", and turn it into this: "1{0}3{1}07{2}". Now we can create loops where we take the characters from the first range and create a new string for each one of them, replacing the {0} with the character. Then, for each one of those strings, we iterate over the second range and create a new string that replaces the {1} placeholder with a character from the second range, and so on and so on until we've created new strings for every possible combination.
public static List<string> GetCombinatins(string input)
{
// Sample input = "1[a-C]3[1-6]07[R,E-G]"
var inputWithPlaceholders = string.Empty; // This will become "1{0}3{1}07{2}"
var placeholder = 0;
var ranges = new List<List<char>>();
for (int i = 0; i < input.Length; i++)
{
// We've found a range start, so replace this with our
// placeholder '{n}' and add the range to our list of ranges
if (input[i] == '[')
{
inputWithPlaceholders += $"{{{placeholder++}}}";
var rangeEndIndex = input.IndexOf("]", i);
ranges.Add(GetRange(input.Substring(i, rangeEndIndex - i)));
i = rangeEndIndex;
}
else
{
inputWithPlaceholders += input[i];
}
}
if (ranges.Count == 0) return new List<string> {input};
// Add strings for the first range
var values = ranges.First().Select(chr =>
inputWithPlaceholders.Replace("{0}", chr.ToString())).ToList();
// Then continually add all combinations of other ranges
for (int i = 1; i < ranges.Count; i++)
{
values = values.SelectMany(value =>
ranges[i].Select(chr =>
value.Replace($"{{{i}}}", chr.ToString()))).ToList();
}
return values;
}
Now with these methods out of the way, we can create output of all our ranges quite easily:
static void Main()
{
Console.WriteLine(string.Join(", ", GetCombinatins("1[a-C]3[1-6]07[R,E-G]")));
GetKeyFromUser("\nPress any key to exit...");
}
Output

I would approach this problem in three stages. The first stage is to transform the source string to an IEnumerable of IEnumerable<string>.
static IEnumerable<IEnumerable<string>> ParseSourceToEnumerables(string source);
For example the source "1[A-C]3[1-6]07[R,E-G]" should be transformed to the 6 enumerables below:
"1"
"A", "B", "C"
"3"
"1", "2", "3", "4", "5", "6"
"07"
"R", "E", "F", "G"
Each literal inside the source has been transformed to an IEnumerable<string> containing a single string.
The second stage would be to create the Cartesian product of these enumerables.
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
IEnumerable<IEnumerable<T>> sequences)
The final (and easiest) stage would be to concatenate each one of the inner IEnumerable<string> of the Cartesian product to a single string. For example
the sequence "1", "A", "3", "1", "07", "R" to the string "1A3107R"
The hardest stage is the first one, because it involves parsing. Below is a partial implementation:
static IEnumerable<IEnumerable<string>> ParseSourceToEnumerables(string source)
{
var matches = Regex.Matches(source, #"\[(.*?)\]", RegexOptions.Singleline);
int previousIndex = 0;
foreach (Match match in matches)
{
var previousLiteral = source.Substring(
previousIndex, match.Index - previousIndex);
if (previousLiteral.Length > 0)
yield return Enumerable.Repeat(previousLiteral, 1);
yield return SinglePatternToEnumerable(match.Groups[1].Value);
previousIndex = match.Index + match.Length;
}
var lastLiteral = source.Substring(previousIndex, source.Length - previousIndex);
if (lastLiteral.Length > 0) yield return Enumerable.Repeat(lastLiteral, 1);
}
static IEnumerable<string> SinglePatternToEnumerable(string pattern)
{
// TODO
// Should transform the pattern "X,A-C,YZ"
// to the sequence ["X", "A", "B", "C", "YZ"]
}
The second stage is hard too, but solved. I just grabbed the implementation from Eric Lippert's blog.
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
accumulator.SelectMany(_ => sequence,
(accseq, item) => accseq.Append(item)) // .NET Framework 4.7.1
);
}
The final stage is just a call to String.Join.
var source = "1[A-C]3[1-6]07[R,E-G]";
var enumerables = ParseSourceToEnumerables(source);
var combinations = CartesianProduct(enumerables);
foreach (var combination in combinations)
{
Console.WriteLine($"Combination: {String.Join("", combination)}");
}

Related

Match existing characters together into words and check if the words appear in the given word list?

I have a list of words like this:
string[] listWords = "la,lam,lan,son,som,some,mos,mao,sehi,noesrh,nroeh,doise".Split(',');
The above list words is a combination of characters and they all have meanings. We can temporarily call it a dictionary.
Next, I have a multiple lists of character arrays like this:
string[] charArr1 = "a,j,s".Split(',');
string[] charArr2 = "c,l,o".Split(',');
string[] charArr3 = "d,m,n".Split(',');
string[] charArr4 = "n,e,w".Split(',');
string[] charArr5 = "f,o,x".Split(',');
string[] charArr6 = "h,q,z".Split(',');
string[] charArr7 = "i,r".Split(',');
I want to concatenate characters together. For each charArray I will take 1 character out and concatenate them together to become words, then I will check if these concatenated words are in the listwords[] list or not. If it is present, I will save the word in the saveWords[] array.
Condition:
Characters of the same charArray[] are not concatenated together and each charArray[] can only select one single character each time.
Match all cases and not miss any cases.
Eg:
a+c -> ac (Match correctly) -> search in listwords[] -> does not appear
a+j (Improper matching)
a+s (Improper matching)
a+c+d -> acd (Match properly) -> search in listwords[] -> does not appear
s+e+i+h -> seih (Match correctly) -> search in listwords[] -> does not appear
s+e+h+i -> sehi (Match correctly) -> search in listwords[] -> if this word appears-> save to saveWords[] array
What I mean is that the concatenation of characters will not miss any cases. Eg:
charArr1[]+charArr2[] -> will match the following cases: a+c, a+l, a+o, j+c, j+l, j+o, s+c, s+l, s+o
charArr2[]+charArr1[] -> will match the following cases: c+a, c+j, c+s, l+a,l+j, l+s,o+a,o+j, o+s
charArr1+charArr2+charArr3
charArr1[]+charArr3[]+charArr2[]
charArr2[]+charArr1[]+charArr3[]
charArr2[]+charArr3[]+charArr1[]
and so on...
Please help me as I am confused in figuring out the algorithm. Thanks a lot.
Given
public static IEnumerable<string[]> Permutate(string[] array, int i, int n)
{
if (i == n)
yield return array;
else
for (var j = i; j <= n; j++)
{
Swap(ref array[i], ref array[j]);
foreach (var s in Permutate(array, i + 1, n))
yield return s;
Swap(ref array[i], ref array[j]);
}
}
public static void Swap(ref string a, ref string b) => (a, b) = (b, a);
public static bool IncMask(string[][] source, int[] mask)
{
for (var i = 0; i < mask.Length; i++)
{
mask[i]++;
if (mask[i] > source[i].Length)
mask[i] = 0;
else
return false;
}
return true;
}
public static IEnumerable<string> Iterate(params string[][] source)
{
var masks = new int[source.Length];
while (true)
{
if (IncMask(source, masks))
break;
var array = masks
.Select((i, j) => (i, j))
.Where(x => x.i != 0)
.Select(x => source[x.j][x.i - 1])
.ToArray();
foreach (var result in Permutate(array, 0, array.Length - 1))
yield return string.Concat(result);
}
}
Usage
var listWords = "la,lam,lan,son,som,some,mos,mao,sehi,noesrh,nroeh,doise".Split(',');
var charArr1 = "a,j,s".Split(',');
var charArr2 = "c,l,o".Split(',');
var charArr3 = "d,m,n".Split(',');
var charArr4 = "n,e,w".Split(',');
var charArr5 = "f,o,x".Split(',');
var charArr6 = "h,q,z".Split(',');
var charArr7 = "i,r".Split(',');
var results = Iterate(charArr1, charArr2, charArr3, charArr4, charArr5, charArr6, charArr7)
.Where(x => listWords.Contains(x));
Console.WriteLine(string.Join(", ", results));
Results
la, lam, mao, som, mos, lan, son, lan, son, some, mao, som, mos, son, son, some, doise, doise, sehi, nroeh, noesrh, nroeh, noesrh
Note, this is a fairly computationally heavy problem, I didn't put much effort into making this efficient, nor cared about duplicates. It could likely be solved many other (more performant) ways.
Also, I have only minimally tested this, so I am not responsible for anyone you maim or otherwise injure with this code. It could be completely wrong ¯\_(ツ)_/¯

How to get an index of a word in string array

I want to get the index of a word in a string array.
for example, the sentence I will input is 'I love you.'
I have words[1] = love, how can I get the position of 'love' is 1? I could do it but just inside the if state. I want to bring it outside. Please help me.
This is my code.
static void Main(string[] args)
{
Console.WriteLine("sentence: ");
string a = Console.ReadLine();
String[] words = a.Split(' ');
List<string> verbs = new List<string>();
verbs.Add("love");
int i = 0;
while (i < words.Length) {
foreach (string verb in verbs) {
if (words[i] == verb) {
int index = i;
Console.WriteLine(i);
}
} i++;
}
Console.ReadKey();
}
I could do it but just inside the if state. I want to bring it outside.
Your code identifies the index correctly, all you need to do now is storing it for use outside the loop.
Make a list of ints, and call Add on it for the matches that you identify:
var indexes = new List<int>();
while (i < words.Length) {
foreach (string verb in verbs) {
if (words[i] == verb) {
int index = i;
indexes.Add(i);
break;
}
}
i++;
}
You can replace the inner loop with a call of Contains method, and the outer loop with a for:
for (var i = 0 ; i != words.Length ; i++) {
if (verbs.Contains(words[i])) {
indexes.Add(i);
}
}
Finally, the whole sequence can be converted to a single LINQ query:
var indexes = words
.Select((w,i) => new {w,i})
.Where(p => verbs.Contains(p.w))
.Select(p => p.i)
.ToList();
Here is an example
var a = "I love you.";
var words = a.Split(' ');
var index = Array.IndexOf(words,"love");
Console.WriteLine(index);
private int GetWordIndex(string WordOrigin, string GetWord)
{
string[] words = WordOrigin.Split(' ');
int Index = Array.IndexOf(words, GetWord);
return Index;
}
assuming that you called the function as GetWordIndex("Hello C# World", "C#");, WordOrigin is Hello C# World and GetWord is C#
now according to the function:
string[] words = WordsOrigin.Split(' '); broke the string literal into an array of strings where the words would be split for every spaces in between them. so Hello C# World would then be broken down into Hello, C#, and World.
int Index = Array.IndexOf(words, GetWord); gets the Index of whatever GetWord is, according to the sample i provided, we are looking for the word C# from Hello C# World that is then splitted into an Array of String
return Index; simply returns whatever index it was located from

Conditionally sum values in a list of tuples

Hello what I'm trying to do is hopefully simple, I have a List<tuple>(string, decimal) and I'm trying to grab the sum of the decimal values if there are two similar strings.
My list contains the values:
("q", .5)
("w", 1.5)
("e", .7)
("r", .8)
("q", .5)
The sum of all the values would therefore be .5 + 1.5 + .7 + .8 + .5 = 4.0.
I assume the algorithm would be something like this:
newlist.select(item1 , item2).where(get the sum of first "q" up to the next "q" in list)
As for existing code, I don't have much only the declaration of the list and it's values.
**I want the sum between 'q' and 'q' + the values of 'q' and 'q', not just in-between, the answer should be '4' and not 3, I want everything between q and q including q's values, thank you.
You could use simple Linq extensions, SkipWhile and TakeWhile
List<Tuple<string,double>> items = new List<Tuple<string,double>>()
{
new Tuple<string,double>("q", .5),
new Tuple<string,double>("w", 1.5),
new Tuple<string,double>("e", .7),
new Tuple<string,double>("r", .8),
new Tuple<string,double>("q", .5)
};
var sumvalue = items.Sum(c=>c.Item2); // Calculates sum of all values
var betweensum = items.SkipWhile(x=>x.Item1 == "q") // Skip until matching item1
.TakeWhile(x=>x.Item1 != "q") // take until matching item1
.Sum(x=>x.Item2); // Sum
As asked in the comments, in case if you have multiple such sets and you want count in between those matching strings for multiple sets, do this.
int gid = 0;
items.Select(c => new { Tuple = c, gid = c.Item1=="q"? ++gid : gid })
.GroupBy(x=>x.gid)
.Where(x=>x.Key%2==1)
.SelectMany(x=>x.Skip(1))
.Sum(x=>x.Tuple.Item2);
Working Demo
You can add a new IEnumerable extension to get all values between two conditions. Note that if there is no second occurrence of the condition an empty list is returned, this can be edited based on your requirements:
public static class Extensions
{
public static IEnumerable<T> TakeBetween<T>(this IEnumerable<T> e, Func<T, bool> f)
{
bool first = false;
List<T> ret = new List<T>();
foreach (var item in e)
{
if(f(item) && !first)
{
first = true;
ret.Add(item);
continue;
}
if(first)
{
ret.Add(item);
if(f(item))
return ret;
}
}
return new List<T>();
}
}
Then it is just a matter of using this to get the sum:
var sum = list.TakeBetween(x => x.Item1 == "q").Sum(x => x.Item2);

Find the longest repetition character in String

Let's say i have a string like
string text = "hello dear";
Then I want to determine the longest repetition of coherented characters - in this case it would be ll. If there are more than one with the same count, take any.
I have tried to solve this with linq
char rchar = text.GroupBy(y => y).OrderByDescending(y => y.Count()).Select(x => x).First().Key;
int rcount = text.Where(x => x == rchar).Count();
string routput = new string(rchar, rcount);
But this returns ee. Am I on the right track?
Another solution using LINQ:
string text = "hello dear";
string longestRun = new string(text.Select((c, index) => text.Substring(index).TakeWhile(e => e == c))
.OrderByDescending(e => e.Count())
.First().ToArray());
Console.WriteLine(longestRun); // ll
It selects a sequence of substrings starting with the same repeating character, and creates the result string with the longest of them.
Although a regex or custom Linq extension is fine, if you don't mind "doing it the old way" you can achieve your result with two temporary variables and a classic foreach loop.
The logic is pretty simple, and runs in O(n). Loop through your string and compare the current character with the previous one.
If it's the same, increase your count. If it's different, reset it to 1.
Then, if your count is greater than the previous recorded max count, overwrite the rchar with your current character.
string text = "hello dear";
char rchar = text[0];
int rcount = 1;
int currentCount = 0;
char previousChar = char.MinValue;
foreach (char character in text)
{
if (character != previousChar)
{
currentCount = 1;
}
else
{
currentCount++;
}
if (currentCount >= rcount)
{
rchar = character;
rcount = currentCount;
}
previousChar = character;
}
string routput = new string(rchar, rcount);
It's indeed verbose, but gets the job done.
If you prefer doing things with LINQ, you can do it fairly simply with a LINQ extension:
var text = "hello dear";
var result = string.Join("",
text
.GroupAdjacentBy((l, r) => (l == r)) /* Create groups where the current character is
Is the same as the previous character */
.OrderByDescending(g => g.Count()) //Order by the group lengths
.First() //Take the first group (which is the longest run)
);
With this extension (taken from Use LINQ to group a sequence of numbers with no gaps):
public static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> GroupAdjacentBy<T>(this IEnumerable<T> source, Func<T, T, bool> predicate)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
var list = new List<T> { e.Current };
var pred = e.Current;
while (e.MoveNext())
{
if (predicate(pred, e.Current))
{
list.Add(e.Current);
}
else
{
yield return list;
list = new List<T> { e.Current };
}
pred = e.Current;
}
yield return list;
}
}
}
}
Might be a bit overkill - but I've found GroupAdjacentBy quite useful in other situations as well.
RegEx would be a option
string text = "hello dear";
string Result = string.IsNullOrEmpty(text) ? string.Empty : Regex.Matches(text, #"(.)\1*", RegexOptions.None).Cast<Match>().OrderByDescending(x => x.Length).First().Value;

Need algorithm to make simple program (sentence permutations)

I really cant understand how to make a simple algorithm on C# to solve my problem. So, we have a sentences:
{Hello|Hi|Hi-Hi} my {mate|m8|friend|friends}.
So, my program should make a lot of sentences looks like:
Hello my mate.
Hello my m8.
Hello my friend.
Hello my friends.
Hi my mate.
...
Hi-Hi my friends.
I know, there are a lot of programs which could do this, but i'd like to make it myself. Ofcourse, it should work with this too:
{Hello|Hi|Hi-Hi} my {mate|m8|friend|friends}, {i|we} want to {tell|say} you {hello|hi|hi-hi}.
Update I just wasn't too happy about my using the regexen to parse so simple input; yet I disliked the manual index manipulation jungle found in other answers.
So I replaced the tokenizing with a Enumerator-based scanner with two alternating token-states. This is more justified by the complexity of the input, and has a 'Linqy' feel to it (although it really isn't Linq). I have kept the original Regex based parser at the end of my post for interested readers.
This just had to be solved using Eric Lippert's/IanG's CartesianProduct Linq extension method, in which the core of the program becomes:
public static void Main(string[] args)
{
const string data = #"{Hello|Hi|Hi-Hi} my {mate|m8|friend|friends}, {i|we} want to {tell|say} you {hello|hi|hi-hi}.";
var pockets = Tokenize(data.GetEnumerator());
foreach (var result in CartesianProduct(pockets))
Console.WriteLine(string.Join("", result.ToArray()));
}
Using just two regexen (chunks and legs) to do the parsing into 'pockets', it becomes a matter of writing the CartesianProduct to the console :) Here is the full working code (.NET 3.5+):
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.Collections.Generic;
namespace X
{
static class Y
{
private static bool ReadTill(this IEnumerator<char> input, string stopChars, Action<StringBuilder> action)
{
var sb = new StringBuilder();
try
{
while (input.MoveNext())
if (stopChars.Contains(input.Current))
return true;
else
sb.Append(input.Current);
} finally
{
action(sb);
}
return false;
}
private static IEnumerable<IEnumerable<string>> Tokenize(IEnumerator<char> input)
{
var result = new List<IEnumerable<string>>();
while(input.ReadTill("{", sb => result.Add(new [] { sb.ToString() })) &&
input.ReadTill("}", sb => result.Add(sb.ToString().Split('|'))))
{
// Console.WriteLine("Expected cumulative results: " + result.Select(a => a.Count()).Aggregate(1, (i,j) => i*j));
}
return result;
}
public static void Main(string[] args)
{
const string data = #"{Hello|Hi|Hi-Hi} my {mate|m8|friend|friends}, {i|we} want to {tell|say} you {hello|hi|hi-hi}.";
var pockets = Tokenize(data.GetEnumerator());
foreach (var result in CartesianProduct(pockets))
Console.WriteLine(string.Join("", result.ToArray()));
}
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item}));
}
}
}
Old Regex based parsing:
static readonly Regex chunks = new Regex(#"^(?<chunk>{.*?}|.*?(?={|$))+$", RegexOptions.Compiled);
static readonly Regex legs = new Regex(#"^{((?<alternative>.*?)[\|}])+(?<=})$", RegexOptions.Compiled);
private static IEnumerable<String> All(this Regex regex, string text, string group)
{
return !regex.IsMatch(text)
? new [] { text }
: regex.Match(text).Groups[group].Captures.Cast<Capture>().Select(c => c.Value);
}
public static void Main(string[] args)
{
const string data = #"{Hello|Hi|Hi-Hi} my {mate|m8|friend|friends}, {i|we} want to {tell|say} you {hello|hi|hi-hi}.";
var pockets = chunks.All(data, "chunk").Select(v => legs.All(v, "alternative"));
The rest is unchanged
Not sure what you need Linq (#user568262) or "simple" recursion (#Azad Salahli) for. Here's my take on it:
using System;
using System.Text;
class Program
{
static Random rng = new Random();
static string GetChoiceTemplatingResult(string t)
{
StringBuilder res = new StringBuilder();
for (int i = 0; i < t.Length; ++i)
if (t[i] == '{')
{
int j;
for (j = i + 1; j < t.Length; ++j)
if (t[j] == '}')
{
if (j - i < 1) continue;
var choices = t.Substring(i + 1, j - i - 1).Split('|');
res.Append(choices[rng.Next(choices.Length)]);
i = j;
break;
}
if (j == t.Length)
throw new InvalidOperationException("No matching } found.");
}
else
res.Append(t[i]);
return res.ToString();
}
static void Main(string[] args)
{
Console.WriteLine(GetChoiceTemplatingResult(
"{Hello|Hi|Hi-Hi} my {mate|m8|friend|friends}, {i|we} want to {tell|say} you {hello|hi|hi-hi}."));
}
}
As others have noted, you can solve your problem by splitting up the string into a sequence of sets, and then taking the Cartesian product of all of those sets. I wrote a bit about generating arbitrary Cartesial products here:
http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx
An alternative approach, more powerful than that, is to declare a grammar for your language and then write a program that generates every string in that language. I wrote a long series of articles on how to do so. It starts here:
http://blogs.msdn.com/b/ericlippert/archive/2010/04/26/every-program-there-is-part-one.aspx
You can use a Tuple to hold index values of each collection.
For example, you would have something like:
List<string> Greetings = new List<string>()
{
"Hello",
"Hi",
"Hallo"
};
List<string> Targets = new List<string>()
{
"Mate",
"m8",
"friend",
"friends"
};
So now you have your greetings, let's create random numbers and fetch items.
static void Main(string[] args)
{
List<string> Greetings = new List<string>()
{
"Hello",
"Hi",
"Hallo"
};
List<string> Targets = new List<string>()
{
"Mate",
"m8",
"friend",
"friends"
};
var combinations = new List<Tuple<int, int>>();
Random random = new Random();
//Say you want 5 unique combinations.
while (combinations.Count < 6)
{
Tuple<int, int> tmpCombination = new Tuple<int, int>(random.Next(Greetings.Count), random.Next(Targets.Count));
if (!combinations.Contains(tmpCombination))
{
combinations.Add(tmpCombination);
}
}
foreach (var item in combinations)
{
Console.WriteLine("{0} my {1}", Greetings[item.Item1], Targets[item.Item2]);
}
Console.ReadKey();
}
This doesn't look trivial. You need to
1. do some parsing, to extract all the lists of words that you want to combine,
2. obtain all the actual combinations of these words (which is made harder by the fact that the number of lists you want to combine is not fixed)
3. rebuild the original sentence putting all the combinations in the place of the group they came from
part 1 (the parsing part) is probably the easiest: it could be done with a Regex like this
// get all the text within {} pairs
var pattern = #"\{(.*?)\}";
var query = "{Hello|Hi|Hi-Hi} my {mate|m8|friend|friends}.";
var matches = Regex.Matches(query, pattern);
// create a List of Lists
for(int i=0; i< matches.Count; i++)
{
var nl = matches[i].Groups[1].ToString().Split('|').ToList();
lists.Add(nl);
// build a "template" string like "{0} my {1}"
query = query.Replace(matches[i].Groups[1].ToString(), i.ToString());
}
for part 2 (taking a List of Lists and obtain all resulting combinations) you can refer to this answer
for part 3 (rebuilding your original sentence) you can now take the "template" string you have in query and use String.Format to substitute all the {0}, {1} .... with the combined values from part 2
// just one example,
// you will need to loop through all the combinations obtained from part 2
var OneResultingCombination = new List<string>() {"hi", "mate"};
var oneResult = string.Format(query, OneResultingCombination.ToArray());

Categories