Visual Studio C# view output - c#

Working through a string deduplicating and finding that when I rebuild and run the console closes and I don't see what the string is.
using System;
using System.Collections.Generic;
using System.Linq;
namespace StringPrintDuplicates
{
class Program
{
public static void DedupString(List<string> duplicatesString)
{
List<string> distinctList = duplicatesString.Distinct().ToList();
foreach(string deduped in distinctList) {
Console.WriteLine("{0}",deduped);
}
}
static void Main()
{
List<string> list = new List<string>();
list.Add("t");
list.Add("a");
list.Add("l");
list.Add("k");
list.Add("j");
list.Add("l");
list.Add("l");
list.Add("k");
list.Add("k");
list.Add("s");
list.Add("s");
list.Add("h");
list.Add("h");
list.Add("o");
list.Add("e");
Console.WriteLine("The input is: {0} ", list.ToString());
DedupString(list);
}
}
}
When I run this I don't get an errors and the output should be the letters that are duplicated in the list. I am using 2019 and configured to stop console when debugging and also not use "Just my code" but I see nothing. This should return "lksh".

StyleZ and Chandru are correct - . As an aside, as of C#3, you can initialize your lists like this:
var list = new List<string>() { "t", "a", "l", "k", "j", "l", "l", "k", "k", "s", "s", "h", "h", "o", "e" };
This may be easier to write and read.

There are multiple problems I see right after I noticed the code.
Distinct() is a keyword to remove all duplicates
Your list.ToString() will definitely not work, the reason why is that you want to concat all strings together. For this, you should use Aggregate().
If you really want to return just those, that are duplicates, you want to do something like this:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
public static void DedupString(List<string> duplicatesString)
{
List<string> distinctList = duplicatesString.Distinct().ToList();
foreach(string deduped in distinctList) {
Console.Write("{0}",deduped);
}
Console.WriteLine("");
}
public static void DuplicitsOnly(List<string> duplicatesString)
{
var distinctList = duplicatesString.GroupBy(x => x).Where(x => x.Count() > 1).Select(x => x.Key).ToList();
foreach(string deduped in distinctList) {
Console.Write("{0}",deduped);
}
Console.WriteLine("");
}
static void Main()
{
List<string> list = new List<string>();
list.Add("t");
list.Add("a");
list.Add("l");
list.Add("k");
list.Add("j");
list.Add("l");
list.Add("l");
list.Add("k");
list.Add("k");
list.Add("s");
list.Add("s");
list.Add("h");
list.Add("h");
list.Add("o");
list.Add("e");
Console.WriteLine("The input is: {0} ", list.Aggregate("", (x, y) => x + y));
DedupString(list);
DuplicitsOnly(list);
}
}
}
Also, about the Console closing ... I had a same problem ... at the right down corner, you should see "Application Output"... I dont know how to fix it for sure, but this was the way I found the console :) ( or just smash a ReadLine(); at the en of a file ... that one should work too )

Alternative way (maybe simpler)
1) You can modify DedupString method like below for showing only duplicate entries:
public static void DedupString(List<string> duplicatesString)
{
List<string> distinctList = duplicatesString.Distinct().ToList();
foreach (string deduped in distinctList)
{
if(duplicatesString.FindAll(x => x.Equals(deduped)).Count > 1)
{
Console.WriteLine("{0}", deduped);
}
}
}
2) Add Console.ReadLine(); at the end of Main() to prevent console from closing. Only after pressing enter key, console will close.

Related

Combinations from different groups in a specific order

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))))));

Trying to compare two lists c# - Should work?

I have a method that looks like this:
GetDrawing(Dictionary<string, List<string>> AllDrawings, Dictionary<string, bool> ImportData, string[] ItemsToCompare)
Firstly, the method creates a new List<string> with all the items from ImportData<string, bool> that have the value true and can be found in string[] ItemsToCompare
Secondly, I would like to compare the new List<string> with the List from AllDrawings<string, List<string>>. The method is in the end supposed to return a string with the key from AllDrawings<string>, List<String>> where the two lists match.
I have now spent a lot of hours trying to figure this out myself and also tried every answer I could find to similar questions here on Stackoverflow but with no luck.
Below is the full code for my method. As mentioned above I've tried a lot of different methods to compare the lists but the one following below is the latest try.
public static string GetDrawing(Dictionary<string, List<string>> AllDrawings, Dictionary<string, bool> ImportData, string[] ItemsToCompare)
{
string FinalDrawing = "";
try
{
List<string> AllCorrect = new List<string>();
foreach (var item in ImportData)
{
if (item.Value == true && ItemsToCompare.Contains(item.Key))
AllCorrect.Add(item.Key);
}
AllCorrect.Sort();
foreach (var DrawItem in AllDrawings)
{
DrawItem.Value.Sort();
var match = AllCorrect.SequenceEqual(DrawItem.Value);
if (match == true)
{
FinalDrawing = DrawItem.Key;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return FinalDrawing;
}
My problem is that the return value from var match = AllCorrect.SequenceEqual(DrawItem.Value); is false and therefore the FinalDrawing is never set.
All answers are very appreciated.
Thanks in advance!
Ok.. I already said that in comments, but just to make sure that you don't get yelled at too much for not-using-linq and such:
your program seems correct up to the point you told us.
Here's a simple test. I provided some stub data that cover all things that you seem to check:
only true things from importedata
only things that are listed in itemstocompare
input lists are not sorted
-> http://rextester.com/HAE73942
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
public class Program
{
// your original function, nothing changed
public static string GetDrawing(Dictionary<string, List<string>> AllDrawings, Dictionary<string, bool> ImportData, string[] ItemsToCompare)
{
string FinalDrawing = "";
try
{
List<string> AllCorrect = new List<string>();
foreach (var item in ImportData)
{
if (item.Value == true && ItemsToCompare.Contains(item.Key))
AllCorrect.Add(item.Key);
}
AllCorrect.Sort();
foreach (var DrawItem in AllDrawings)
{
DrawItem.Value.Sort();
var match = AllCorrect.SequenceEqual(DrawItem.Value);
if (match == true)
{
FinalDrawing = DrawItem.Key;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return FinalDrawing;
}
public static void Main(string[] args)
{
var allDrawings = new Dictionary<string, List<string>>();
allDrawings.Add("aaa", new List<string>{ "a03", "a01", "a02" }); // originally unsorted
allDrawings.Add("bbb", new List<string>{ "b03", "b01", "b02" }); // originally unsorted
allDrawings.Add("ccc", new List<string>{ "c03", "c01", "c02" }); // originally unsorted
var import = new Dictionary<string, bool>();
import.Add("b01", false); // falsey
import.Add("a05", true); // not in comparison
import.Add("a03", true);
import.Add("c01", false); // falsey
import.Add("a02", true);
import.Add("a04", true); // not in comparison
import.Add("a01", true);
var toCompare = new string[9];
toCompare[0]="a01"; toCompare[1]="a02"; toCompare[2]="a03";
toCompare[3]="b01"; toCompare[4]="b02"; toCompare[5]="b03";
toCompare[6]="c01"; toCompare[7]="c02"; toCompare[8]="c03";
var result = GetDrawing(allDrawings, import, toCompare);
Console.WriteLine("Result: " + result);
}
}
it works fine and prints aaa as it should.
This means that you had to have overlooked something in the input data. Maybe some strings are uppercase/lowercase? Maybe some strings have whitespaces inside and others not?
This code :
List<string> AllCorrect = new List<string>();
foreach (var item in ImportData)
{
if (item.Value == true && ItemsToCompare.Contains(item.Key))
AllCorrect.Add(item.Key);
}
AllCorrect.Sort();
Can be reduced to :
List<string> AllCorect = ImportData.Where(vp =>
ItemsToCompare.Contains(vp.Key) && vp.Value).Select(vp => vp.Key).OrderBy(vp => vp).ToList();
To solve the second problem you can do :
return AllDrawings.First(l => l.Value.OrderBy(l2 => l2).SequenceEqual(AllCorect)).Key;
P.S.
If First() always throws an exception then it suggests that problem lays in how this lists are filled with values and this is a different question.
Example :
public static string GetDrawing(Dictionary<string, List<string>> AllDrawings, Dictionary<string, bool> ImportData, string[] ItemsToCompare)
{
List<string> AllCorect = ImportData.Where(vp =>
ItemsToCompare.Contains(vp.Key) && vp.Value).Select(vp => vp.Key).OrderBy(vp => vp).ToList();
return AllDrawings.First(l => l.Value.OrderBy(l2 => l2).SequenceEqual(AllCorect)).Key;
}
static void Main(string[] args)
{
List<string> list1 = new List<string>() { "one", "two", "three" };
List<string> list2 = new List<string>() { "five", "six", "seven" };
Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>()
{
{"first", list1}, {"second", list2}
};
string[] itemsToCompare = { "one", "two", "three" };
var dict2 = new Dictionary<string, bool>()
{
{"one", true},
{"two", true},
{"three", true}
};
var result = GetDrawing(dict, dict2, itemsToCompare);
Console.WriteLine(result);
}
Output : first
If the strings really match also for casing your code would be correct. I propose you inspect your string sequences - create an readable string and add appropriate break points. Also try to sort case-insensitive if that is what is missing here
AllCorrect.Sort(StringComparer.InvariantCultureIgnoreCase);
var AllCorrectInfo = string.Join(", ", AllCorrect.ToArray());
foreach (var DrawItem in AllDrawings)
{
DrawItem.Value.Sort();
var DrawItemInfo = string.Join(", ", DrawItem.Value.ToArray());
var match = AllCorrect.SequenceEqual(DrawItem.Value, StringComparer.InvariantCultureIgnoreCase);
if (match == true)
{
FinalDrawing = DrawItem.Key;
}
}

Can anyone tell how to program a Binary Search in Windows Forms pls?

Does anyone know how to make a binary search in windows form application (C#) without doing the insert part? I need to search from a list of allItems.
Try This it shows the index output:-
using System;
using System.Collections.Generic;
namespace BinarySearchmethod
{
class AKSHAY
{
static void Main(string[] args)
{
List<string> list = new List<string>() { "A", "B", "C", "D", "E", "F", "G" };
int index1 = list.BinarySearch("C");
Console.WriteLine("Index 1: {0}", index1);
int index2 = list.BinarySearch("F");
Console.WriteLine("Index 2 : {0} ", index2);
int index3 = list.BinarySearch("H");
Console.WriteLine("Index 3 : {0} ", index3);
// wait for input before exiting
Console.WriteLine("Press enter to finish");
Console.ReadLine();
}
}
}
Output:-
Binary Search Tree Implementation in C#
Have a look at the List class it has a binary search method that returns an index. https://msdn.microsoft.com/en-us/library/w4e7fxsh%28v=vs.110%29.aspx.

c# dictionary and forloop/writing to text messing up - how to make it go through once and not through every iteration

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
namespace ConsoleApplication9
{
class main
{
private static StreamWriter objWriter = new StreamWriter(#"D:\text1.txt", true);
private static StreamWriter tripWriter = new StreamWriter(#"D:\text2.txt", true);
private static void Main()
{
WelcomeMenu();
}
public static void WelcomeMenu()
{
Console.WriteLine("Welcome to Pow Drop Log v1");
Console.WriteLine("A. Press A to start the trip");
while (true)
{
string enteredWelcome = Console.ReadLine();
{
if (enteredWelcome == "a")
{
List();
}
else
{
Console.WriteLine("That is an invalid option");
continue;
}
}
}
}
public static void WriteToTextFile(string text)
{
objWriter.Write(text);
objWriter.Flush();
}
public static void WriteToCurrentTrip(string text)
{
tripWriter.Write(text);
tripWriter.Flush();
}
public static void List()
{
Stopwatch Triptime = new Stopwatch();
Triptime.Start();
Console.WriteLine("You have started the trip");
Dictionary<string, string> tdItems = new Dictionary<string, string>();
tdItems.Add("1", "foo");
tdItems.Add("2", "bar");
tdItems.Add("3", "bar");
tdItems.Add("4", "end");
while (true)
{
string[] items = Console.ReadLine().Split(' ');
string result = null;
int result1;
TimeSpan timeSpan = Triptime.Elapsed; string time = string.Format("{0:00}:{1:00}:{2:00}", timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
foreach (string itemNumber in items)
{
if (tdItems.ContainsKey(itemNumber) && (int.TryParse(itemNumber, out result1)))
{
result += " + " + tdItems[itemNumber];
WriteToTextFile(tdItems[itemNumber] + Environment.NewLine);
WriteToCurrentTrip(tdItems[itemNumber] + Environment.NewLine);
}
if (!tdItems.ContainsKey(itemNumber) && (int.TryParse(itemNumber, out result1)))
{
Console.WriteLine("You have entered a drop which is not in the database, Try again");
continue;
}
else if (itemNumber == "end")
{
Triptime.Stop();
Console.WriteLine("End of Trip");
Console.WriteLine("Elapsed time " + time);
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
WelcomeMenu();
break;
}
}
}
}
}
}
The code seems to be going through each part instead of once.
Say if I typed 1 2 3 in the console, in the text file it would write
foo
foo bar
foo bar baz
instead of it writing foo + bar + baz in the first place.
I debugged it and it shows it keeps going through, how would I be able to fix this and make it so it does it correctly?.
Thanks!
This code is very bad, and I recommend basically rewriting it until it is much shorter and simpler. But if I had to make the smallest change possible to make it print out the right thing, I'd change this:
WriteToTextFile(result.Substring(3) + Environment.NewLine);
WriteToCurrentTrip(result.Substring(3) + Environment.NewLine);
To this:
WriteToTextFile(tdItems[itemNumber] + Environment.NewLine);
WriteToCurrentTrip(tdItems[itemNumber] + Environment.NewLine);
EDIT: For reference, here's roughly how I'd write what you indicated you wanted your List function to do, with comments interleaved. You could make it nicer yet, but this should show you some useful things. I hope I got the intended behavior right.
// StartNew happens to be a quicker way to create and initialize a Stopwatch
Stopwatch triptime = Stopwatch.StartNew();
Console.WriteLine("You have started the trip");
// you can use collection initializer syntax to make it nicer to read when
// you want to add lots of things to a data structure
var tdItems = new Dictionary<string, string> {
{ "1", "foo" },
{ "2", "bar" },
{ "3", "baz" },
{ "4", "end" },
};
while (true)
{
string[] items = Console.ReadLine().Split(' ');
// you can use format strings to easily customize the stringification
// of DateTime and TimeSpan objects, see the MSDN docs
string time = string.Format("{0:c}", triptime.Elapsed);
List<string> waypoints = new List<string>();
// it's easiest to first find the destinations your trip has to visit
foreach (string itemNumber in items)
{
if (tdItems.ContainsKey(itemNumber))
waypoints.Add(tdItems[itemNumber]);
else
Console.WriteLine(
"You have entered a drop which is not in the database...");
}
// string.Join is an easy way to avoid worrying about putting an extra
// "+" at the front or end
string tripDescription = string.Join(" + ", waypoints);
// "using" is generally important so you don't hold a lock on the file
// forever, and it saves you from manually calling "flush" -- it
// flushes when the writers are disposed at the end of the using block
using (var objWriter = new StreamWriter(#"D:\text1.txt", true))
using (var tripWriter = new StreamWriter(#"D:\text2.txt", true))
{
// WriteLine adds a newline for you
objWriter.WriteLine(tripDescription);
tripWriter.WriteLine(tripDescription);
}
if (waypoints.Contains("end"))
{
Console.WriteLine("End of Trip");
Console.WriteLine("Elapsed time " + time);
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
break;
}
}
// you'll get the welcome menu again when you return from this function
If you break up what your code is doing into small, checkable chunks, it's a lot easier to understand, verify, and debug. In my version, you can see that it got the right waypoints; then you can see that the description looks right; then you can see that it wrote to the file. In yours, it's sort of trying to do everything at the same time, so it's harder to work with.

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