Combinations from different groups in a specific order - c#

I have a list of parameters that each accept a specific range of inputs. I am working on creating a test that creates every possible valid input. Furthermore, each group is optional (can be skipped entirely), so the combination lengths don't necessarily have to be the same length of the list.
Input
List<string[]> temp = new List<string[]>
{
// The order of these groups is important
new string[] { "a", "b", "c" },
new string[] { "d", "e" },
new string[] { "f", "g", "h" }
};
Constraints
0 or 1 item per group (a string[] above)
Order of the List<T> must be preserved
Valid Combinations
a, e, f
a, d, g
c, e, f
b, g
c, f
Invalid Combinations
a, b, f (a and b are from the same group - not allowed)
a, f, d (wrong order - d must come before f)
So far, I have gone back to my library where I have a Combinations LINQ method.
public static class IEnumerableExtensions
{
// Can be used to get all permutations at a certain level
// Source: http://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n#1898744
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] { e }).Concat(c)));
}
}
In the past I have only used this on single sequence to do things like generate every permutation of URL segments. But I am struggling with its usage on a nested list with the constraints of one per group and in a specific order.
I know I can solve this particular puzzle by doing 3 nested loops and using lists to track which items were already used, but I don't know in advance how many items will be in the List<T>, so that won't work in the general case.
How can I get all of the valid combinations of the above input?
I would prefer LINQ, but will accept any solution that solves this problem.

Using some extension functions,
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> rest, params T[] first) => first.Concat(rest);
public static IEnumerable<T> AsSingleton<T>(this T source) {
yield return source;
}
You can write
public static IEnumerable<IEnumerable<T>> ParametersWithEmpty<T>(this IEnumerable<IEnumerable<T>> ParmLists) {
if (ParmLists.Count() == 0)
yield break; // empty
else {
var rest = ParametersWithEmpty(ParmLists.Skip(1));
foreach (var p in ParmLists.First()) {
yield return p.AsSingleton(); // p.Concat(empty)
foreach (var r in rest)
yield return r.Prepend(p); // p.Concat(r)
}
foreach (var r in rest)
yield return r; // empty.Concat(r)
}
}
You can call it like this:
var ans = temp.ParametersWithEmpty();
To include all the levels in the results, you must skip the empty cases implicit in the above code:
public static IEnumerable<IEnumerable<T>> Parameters<T>(this IEnumerable<IEnumerable<T>> ParmLists) {
if (ParmLists.Count() == 1)
foreach (var p in ParmLists.First())
yield return p.AsSingleton();
else {
var rest = Parameters(ParmLists.Skip(1));
foreach (var p in ParmLists.First()) {
foreach (var r in rest)
yield return r.Prepend(p);
}
}
}
Finally, here is an alternative version that may make it somewhat clearer as it outputs the sequence as if each parameter list is preceded by empty, but also returns the all empty sequence in the answer.
public static IEnumerable<IEnumerable<T>> ParametersWithEmpty2<T>(this IEnumerable<IEnumerable<T>> ParmLists) {
if (ParmLists.Count() == 0)
yield return Enumerable.Empty<T>();
else {
var rest = ParametersWithEmpty2(ParmLists.Skip(1));
foreach (var r in rest)
yield return r; // empty.Concat(r)
foreach (var p in ParmLists.First()) {
foreach (var r in rest)
yield return r.Prepend(p); // p.Concat(r)
}
}
}

Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
public static List<string[]> temp = new List<string[]>
{
// The order of these groups is important
new string[] { "a", "b", "c" },
new string[] { "d", "e" },
new string[] { "f", "g", "h" }
};
static void Main(string[] args)
{
Recursive(0, new List<string>());
Console.ReadLine();
}
public static void Recursive(int level, List<string> output)
{
if (level < temp.Count)
{
foreach (string value in temp[level])
{
List<string> newList = new List<string>();
newList.AddRange(output);
newList.Add(value);
Console.WriteLine(string.Join(",", newList));
Recursive(level + 1, newList);
}
}
}
}
}
To get additional combinations starting with 2nd and 3rd level change main()
static void Main(string[] args)
{
for (int i = 0; i < temp.Count; i++)
{
Recursive(i, new List<string>());
}
Console.ReadLine();
}

Here is a Linq expression that enumerates all combinations. You can try it out in online C# repl. The idea is to use accumulator "a" to gather all combinations by going through each item of your temp collection "b" and add elements of "b" (single item combination) as well as add to every accumulated combination so far. Pretty hairy expression though.
var combinations = temp.Aggregate(Enumerable.Empty<IEnumerable<string>>(), (a, b) => a
.Concat(b.Select(i => Enumerable.Repeat(i, 1)))
.Concat(a.SelectMany(i => b.Select(j => i.Concat(Enumerable.Repeat(j, 1))))));

Related

How do you iterate through a mixed List<t> and print contents to console in C#?

I have a List<object> which contains strings and even additional lists.
List<object> NewArray = new List<object>();
so basically the list contains a mixture....
As a sanity check, I want to print out the contents to the console. I start by iterating through and test to see whether the element is a list. If it isn't then it will be a string and can be printed to the console. If it is a list, I want to iterate through and print the string contents to the console but with a tab to indent it.
I have this so far:
for (int outer = 0; outer < NewArray.Count; outer++)
{
var innerList = NewArray[outer];
if (innerList.GetType().IsGenericType && innerList.GetType().GetGenericTypeDefinition() == typeof(List<>))
{
for (int inner = 0; inner < innerList.Count; inner++)
{
//print string
}
}
else
{
//print string
}
}
I didn't want to use a foreach loop as I'm not sure the order of the list would be guaranteed and will be in the future adding a increment number (which can be provided by the inner and outer variables).
The issue I am getting is an error here:
inner < innerList.Count
which is:
Operator '<' cannot be applied to operands of type 'int' and 'method group'
What do I need to do to overcome this? I'm not sure it is the most efficient way of achieving the end result but....
static void Main()
{
var randomCrap = new List<Object>
{
1, "two",
new List<object> { 3, 4 },
5, 6,
new List<object> {
new List<object> { 7, 8, "nine" },
},
};
randomCrap.PrintAll();
}
Output:
1
two
3
4
5
6
7
8
nine
Using this:
public static class Extensions
{
public static void PrintAll(this Object root)
{
foreach (var x in root.SelectAll())
{
Console.WriteLine(x);
}
}
public static IEnumerable<Object> SelectAll(this object o)
{
// Thank you, eocron
if (o is String)
{
yield return o;
}
else if (o is IEnumerable)
{
var e = o as IEnumerable;
foreach (var child in e)
{
foreach (var child2 in child.SelectAll())
yield return child2;
}
}
else
{
yield return o;
}
}
}
If you know your object is a List<> of some generic type, you could always cast to IList and loop through it that way.
To paraphrase your code:
if (innerList.GetType().IsGenericType && innerList.GetType().GetGenericTypeDefinition() == typeof(List<>))
{
var list = (IList)innerList;
for (int inner = 0; inner < list.Count; inner++)
{
Console.WriteLine(list[inner].ToString());
//print string
}
}
But in reality, you should be doing what itsme86 said, and making strong types with an overriden ToString() or Display() method.
Here is more simplistic way of doing what you want:
public static void DeepPrint(object obj, int recursionLevel)
{
if(obj == null)
{
//print null
return;
}
var str = obj as string;
if(str != null)
{
//print str
return;
}
var enumer = obj as IEnumerable;
if(enumer != null)
{
foreach(var e in enumer)
{
DeepPrint(e, recursionLevel+1);
}
return;
}
//print obj.ToString();
}
Then call it like this on whatever you desire:
DeepPrint(myObjectOrList, 0);
PS
For those who say about "random crap" - embrace the string.Format(...), embrace serialization in general, embrace WCF and dynamic, and etc. There is many random things in this world, what doesn't really need strong typing. In fact it will just become "crap" if you provide strong typing to some common used functions.
One way you could do it is to check if the object implements ICollection, and if so, iterate over the contents. I created a recursive method to handle cases where a collection contained other collections, which includes an indentAmount argument, so that nested collections are indented by a tab each time they're encountered:
public static void PrintItem(object item, int indentAmount = 0)
{
var indent = new string('\t', indentAmount);
if (item == null) Console.WriteLine($"{indent}<null>");
if (item is ICollection)
{
var innerItems = item as IEnumerable;
Console.WriteLine($"{indent}Collection type encountered:");
indentAmount++;
foreach (var innerItem in innerItems)
{
PrintItem(innerItem, indentAmount);
}
}
else
{
Console.WriteLine($"{indent}{item}");
}
}
Usage
private static void Main()
{
var items = new List<object>
{
"first",
2,
new List<string> {"listFirst", "listSecond"},
new[] {"arrayFirst", "arraySecond"},
new ArrayList {"arrayListFirst", "arrayListSecond"},
"third",
new List<List<string>>
{
new List<string> {"nestedList1First", "nestedList1Second"},
new List<string> {"nestedList2First", "nestedList2Second"}
},
4f,
new object[] {5d, "six", new List<object>{"seven", 8} },
9,
"ten"
};
PrintItem(items);
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output

algorithm for selecting N random elements from a List<T> in C# [duplicate]

This question already has answers here:
Randomize a List<T>
(28 answers)
Closed 6 years ago.
I need a quick algorithm to select 4 random elements from a generic list. For example, I'd like to get 4 random elements from a List and then based on some calculations if elements found not valid then it should again select next 4 random elements from the list.
You could do it like this
public static class Extensions
{
public static Dictionary<int, T> GetRandomElements<T>(this IList<T> list, int quantity)
{
var result = new Dictionary<int, T>();
if (list == null)
return result;
Random rnd = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < quantity; i++)
{
int idx = rnd.Next(0, list.Count);
result.Add(idx, list[idx]);
}
return result;
}
}
Then use the extension method like this:
List<string> list = new List<string>() { "a", "b", "c", "d", "e", "f", "g", "h" };
Dictionary<int, string> randomElements = list.GetRandomElements(3);
foreach (KeyValuePair<int, string> elem in randomElements)
{
Console.WriteLine($"index in original list: {elem.Key} value: {elem.Value}");
}
something like that:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
int n = 4;
var rand = new Random();
var randomObjects = new List<int>();
for (int i = 0; i<n; i++)
{
var index = rand.Next(list.Count);
randomObjects.Add(list[index]);
}
}
}
You can store indexes in some list to get non-repeated indexes:
List<T> GetRandomElements<T>(List<T> allElements, int randomCount = 4)
{
if (allElements.Count < randomCount)
{
return allElements;
}
List<int> indexes = new List<int>();
// use HashSet if performance is very critical and you need a lot of indexes
//HashSet<int> indexes = new HashSet<int>();
List<T> elements = new List<T>();
Random random = new Random();
while (indexes.Count < randomCount)
{
int index = random.Next(allElements.Count);
if (!indexes.Contains(index))
{
indexes.Add(index);
elements.Add(allElements[index]);
}
}
return elements;
}
Then you can do some calculation and call this method:
void Main(String[] args)
{
do
{
List<int> elements = GetRandomelements(yourElements);
//do some calculations
} while (some condition); // while result is not right
}
Suppose that the length of the List is N. Now suppose that you will put these 4 numbers in another List called out. Then you can loop through the List and the probability of the element you are on being chosen is
(4 - (out.Count)) / (N - currentIndex)
funcion (list)
(
loop i=0 i < 4
index = (int) length(list)*random(0 -> 1)
element[i] = list[index]
return element
)
while(check == false)
(
elements = funcion (list)
Do some calculation which returns check == false /true
)
This is the pseudo code, but i think you should of come up with this yourself.
Hope it helps:)
All the answers up to now have one fundamental flaw; you are asking for an algorithm that will generate a random combination of n elements and this combination, following some logic rules, will be valid or not. If its not, a new combination should be produced. Obviously, this new combination should be one that has never been produced before. All the proposed algorithms do not enforce this. If for example out of 1000000 possible combinations, only one is valid, you might waste a whole lot of resources until that particular unique combination is produced.
So, how to solve this? Well, the answer is simple, create all possible unique solutions, and then simply produce them in a random order. Caveat: I will suppose that the input stream has no repeating elements, if it does, then some combinations will not be unique.
First of all, lets write ourselves a handy immutable stack:
class ImmutableStack<T> : IEnumerable<T>
{
public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
private readonly T head;
private readonly ImmutableStack<T> tail;
public int Count { get; }
private ImmutableStack()
{
Count = 0;
}
private ImmutableStack(T head, ImmutableStack<T> tail)
{
this.head = head;
this.tail = tail;
Count = tail.Count + 1;
}
public T Peek()
{
if (this == Empty)
throw new InvalidOperationException("Can not peek a empty stack.");
return head;
}
public ImmutableStack<T> Pop()
{
if (this == Empty)
throw new InvalidOperationException("Can not pop a empty stack.");
return tail;
}
public ImmutableStack<T> Push(T item) => new ImmutableStack<T>(item, this);
public IEnumerator<T> GetEnumerator()
{
var current = this;
while (current != Empty)
{
yield return current.head;
current = current.tail;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
This will make our life easier while producing all combinations by recursion. Next, let's get the signature of our main method right:
public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinationsInRandomOrder<T>(
IEnumerable<T> data, int combinationLength)
Ok, that looks about right. Now let's implement this thing:
var allCombinations = GetAllPossibleCombinations(data, combinationLength).ToArray();
var rnd = new Random();
var producedIndexes = new HashSet<int>();
while (producedIndexes.Count < allCombinations.Length)
{
while (true)
{
var index = rnd.Next(allCombinations.Length);
if (!producedIndexes.Contains(index))
{
producedIndexes.Add(index);
yield return allCombinations[index];
break;
}
}
}
Ok, all we are doing here is producing random indexees, checking we haven't produced it yet (we use a HashSet<int> for this), and returning the combination at that index.
Simple, now we only need to take care of GetAllPossibleCombinations(data, combinationLength).
Thats easy, we'll use recursion. Our bail out condition is when our current combination is the specified length. Another caveat: I'm omitting argument validation throughout the whole code, things like checking for null or if the specified length is not bigger than the input length, etc. should be taken care of.
Just for the fun, I'll be using some minor C#7 syntax here: nested functions.
public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinations<T>(
IEnumerable<T> stream, int length)
{
return getAllCombinations(stream, ImmutableStack<T>.Empty);
IEnumerable<IEnumerable<T>> getAllCombinations<T>(IEnumerable<T> currentData, ImmutableStack<T> combination)
{
if (combination.Count == length)
yield return combination;
foreach (var d in currentData)
{
var newCombination = combination.Push(d);
foreach (var c in getAllCombinations(currentData.Except(new[] { d }), newCombination))
{
yield return c;
}
}
}
}
And there we go, now we can use this:
var data = "abc";
var random = GetAllPossibleCombinationsInRandomOrder(data, 2);
foreach (var r in random)
{
Console.WriteLine(string.Join("", r));
}
And sure enough, the output is:
bc
cb
ab
ac
ba
ca

How to split list into all the cases sublists using LINQ?

I would like to to split list into all the cases SubLists using LINQ?
For example :
List contains : {"a", "b", "c"}
I would like to make list of lists where the result is : {"a", "ab", "abc"}
public List<List<Alphabet>> ListofLists (Stack<String> Pile)
{
var listoflists = new List<List<Alphabet>>();
var list = new List<Alphabet>();
foreach (var temp in from value in Pile where value != "#" select new Alphabet(value))
{
list.Add(temp);
listoflists.Add(list);
}
return listoflists;
}
This method will allow you to do this.
IEnumerable<IEnumerable<T>> SublistSplit<T>(this IEnumerable<T> source)
{
if (source == null) return null;
var list = source.ToArray();
for (int i = 0; i < list.Length; i++)
{
yield return new ArraySegment<T>(list, 0, i);
}
}
In case of strings:
IEnumerable<string> SublistSplit<T>(this IEnumerable<string> source)
{
if (source == null) return null;
var sb = new StringBuilder();
foreach (var x in source)
{
sb.Append(x);
yield return sb.ToString();
}
}
If you want to yield the intermediate values of an accumulation you could define your own extension method:
public IEnumerable<TAcc> Scan<T, TAcc>(this IEnumerable<T> seq, TAcc init, Func<T, TAcc, TAcc> acc)
{
TAcc current = init;
foreach(T item in seq)
{
current = acc(item, current);
yield return current;
}
}
then your example would be:
var strings = new[] {"a", "b", "c"}.Scan("", (str, acc) => str + acc);
for lists, you'll have to copy them each time:
List<Alphabet> input = //
List<List<Alphabet>> output = input.Scan(new List<Alphabet>(), (a, acc) => new List<Alphabet(acc) { a }).ToList();
Note that copying the intermediate List<T> instances could be inefficient, so you may want to consider using an immutable structure instead.

Interleaved merge with LINQ?

I'm currently experimenting a bit with LINQ. Let's say I have two collections of identical length:
var first = new string[] { "1", "2", "3" };
var second = new string[] { "a", "b", "c" };
I would like to merge those two collections into one, but in an interleaved fashion. The resulting sequence should thus be:
"1", "a", "2", "b", "3", "c"
What I've come up with so far is a combination of Zip, an anonymous type and SelectMany:
var result = first.Zip( second, ( f, s ) => new { F = f, S = s } )
.SelectMany( fs => new string[] { fs.F, fs.S } );
Does anybody know of an alternate/simpler way to achieve such an interleaved merge with LINQ?
The example you provided can by made simpler by dispensing with the anonymous type:
var result = first.Zip(second, (f, s) => new[] { f, s })
.SelectMany(f => f);
Warning: this will skip trailing elements if the enumerations have different lengths. If you'd rather substitute in nulls to pad out the shorter collection, use Andrew Shepherd's answer below.
You could write your own Interleave extension method, like in this example.
internal static IEnumerable<T> InterleaveEnumerationsOfEqualLength<T>(
this IEnumerable<T> first,
IEnumerable<T> second)
{
using (IEnumerator<T>
enumerator1 = first.GetEnumerator(),
enumerator2 = second.GetEnumerator())
{
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
yield return enumerator1.Current;
yield return enumerator2.Current;
}
}
}
The given implementation in the accepted answer has an inconsistency:
The resulting sequence will always contain all elements of the first sequence (because of the outer while loop), but if the second sequence contains more elements, than those elements will not be appended.
From an Interleave method I would expect that the resulting sequence contains
only 'pairs' (length of resulting sequence: min(length_1, length_2) * 2)), or that
the remaining elements of the longer sequence are always appended (length of resulting sequence: length_1 + length_2).
The following implementation follows the second approach.
Note the single | in the or-comparison which avoids short-circuit evaluation.
public static IEnumerable<T> Interleave<T> (
this IEnumerable<T> first, IEnumerable<T> second)
{
using (var enumerator1 = first.GetEnumerator())
using (var enumerator2 = second.GetEnumerator())
{
bool firstHasMore;
bool secondHasMore;
while ((firstHasMore = enumerator1.MoveNext())
| (secondHasMore = enumerator2.MoveNext()))
{
if (firstHasMore)
yield return enumerator1.Current;
if (secondHasMore)
yield return enumerator2.Current;
}
}
}
You can just loop and select the array depending on the index:
var result =
Enumerable.Range(0, first.Length * 2)
.Select(i => (i % 2 == 0 ? first : second)[i / 2]);
var result = first.SelectMany( ( f, i ) => new List<string> { f, second[ i ] } );
This is a modified version of the answer from #Douglas. This allows for a dynamic number of IEnumerables to interleave, and goes through all elements, not stopping at the end of the shortest IEnumerable.
public static IEnumerable<T> Interleave<T>(params IEnumerable<T>[] enumerables)
{
var enumerators = enumerables.Select(e => e.GetEnumerator()).ToList();
while (enumerators.Any())
{
enumerators.RemoveAll(e => {
var ended = !e.MoveNext();
if (ended) e.Dispose();
return ended;
});
foreach (var enumerator in enumerators)
yield return enumerator.Current;
}
}

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