C# Remove accent from character? - c#

How can I convert á to a in C#?
For instance: aéíúö => aeiuo
Um, having read those threads [I didn't know they were called diatrics, so I couldn't possible search for that].
I want to "drop" all diatrics but ñ
Currently I have:
public static string RemoveDiacritics(this string text)
{
string normalized = text.Normalize(NormalizationForm.FormD);
var sb = new StringBuilder();
foreach (char c in from c in normalized
let u = CharUnicodeInfo.GetUnicodeCategory(c)
where u != UnicodeCategory.NonSpacingMark
select c)
{
sb.Append(c);
}
return sb.ToString().Normalize(NormalizationForm.FormC);
}
What would be the best way to leave ñ out of this?
My solution was to do the following after the foreach:
var result = sb.ToString();
if (text.Length != result.Length)
throw new ArgumentOutOfRangeException();
int position = -1;
while ((position = text.IndexOf('ñ', position + 1)) > 0)
{
result = result.Remove(position, 1).Insert(position, "ñ");
}
return sb.ToString();
But I'd assume there is a less "manual" way to do this?

if you don´t want remove the ñ, this is a option. It´s fast.
static string[] pats3 = { "é", "É", "á", "Á", "í", "Í", "ó", "Ó", "ú", "Ú" };
static string[] repl3 = { "e", "E", "a", "A", "i", "I", "o", "O", "u", "U" };
static Dictionary<string, string> _var = null;
static Dictionary<string, string> dict
{
get
{
if (_var == null)
{
_var = pats3.Zip(repl3, (k, v) => new { Key = k, Value = v }).ToDictionary(o => o.Key, o => o.Value);
}
return _var;
}
}
private static string RemoveAccent(string text)
{
// using Zip as a shortcut, otherwise setup dictionary differently as others have shown
//var dict = pats3.Zip(repl3, (k, v) => new { Key = k, Value = v }).ToDictionary(o => o.Key, o => o.Value);
//string input = "åÅæÆäÄöÖøØèÈàÀìÌõÕïÏ";
string pattern = String.Join("|", dict.Keys.Select(k => k)); // use ToArray() for .NET 3.5
string result = Regex.Replace(text, pattern, m => dict[m.Value]);
//Console.WriteLine("Pattern: " + pattern);
//Console.WriteLine("Input: " + text);
//Console.WriteLine("Result: " + result);
return result;
}
If you want remove the ñ, the faster option is:
Encoding.ASCII.GetString(Encoding.GetEncoding("Cyrillic").GetBytes(text));

Related

C# find anagram from a string array of candidates

My task is to implement a method that could return a correct sublist of anagrams.
Now so far I am having problems with figuring out how can I collect the anagrams in candidates that match word and return it.
This is my code for now:
public class Anagram
{
public string word;
public Anagram(string sourceWord)
{
if (sourceWord is null)
{
throw new ArgumentNullException(nameof(sourceWord));
}
if (sourceWord.Length == 0)
{
throw new ArgumentException(null);
}
this.word = sourceWord;
}
public string[] FindAnagrams(string[] candidates)
{
if (candidates is null)
{
throw new ArgumentNullException(nameof(candidates));
}
char[] char1 = this.word.ToLower().ToCharArray();
Array.Sort(char1);
string newWord1 = new string(char1);
string newWord2;
string[] result = new string[candidates.Length];
for (int i = 0; i < candidates.Length; i++)
{
char[] char2 = candidates[i].ToLower().ToCharArray();
Array.Sort(char2);
newWord2 = char2.ToString();
if (newWord1 == newWord2)
{
result[i] = candidates[i];
}
}
return result;
}
}
Should I do a second for loop in the if statement or something else.
And by the way how I am doing with my class constructor, It's the first time I am trying to use it and at the end I don't think I called the sourceWord variable correctly..
This is one of my test scenarios I would need to pass later on:
[TestCase("master", new[] { "stream", "pigeon", "maters" }, ExpectedResult = new[] { "stream", "maters" })]
[TestCase("listen", new[] { "enlists", "google", "inlets", "banana" }, ExpectedResult = new[] { "inlets" })]
[TestCase("allergy", new[] { "gallery", "ballerina", "regally", "clergy", "largely", "leading" }, ExpectedResult = new[] { "gallery", "regally", "largely" })]
public string[] FindAnagrams_Detects_Anagrams(string word, string[] candidates)
{
var sut = new Anagram(word);
return sut.FindAnagrams(candidates);
}
Unfortunatly, can't use LINQ on this task.
If two words are anagrams, they have the same numbers of same letters:
art ~ rat ~ tar
we can sort letters within each word and group words by such keys:
...
aaabnn: banana
aemrst: maters, stream
...
Code:
using System.Linq;
...
// Given list of (candidates) word return anagrams
public static IEnumerable<string[]> FindAnagrams(IEnumerable<string> candidates) {
if (null == candidates)
throw new ArgumentNullException(nameof(candidates));
return candidates
.GroupBy(word => string.Concat(word.OrderBy(c => c)))
.Where(group => group.Count() > 1)
.Select(group => group.OrderBy(word => word).ToArray());
}
Demo:
string[] demo = new string[] {
"stream", "pigeon", "maters",
"enlists", "google", "inlets", "banana",
"gallery", "ballerina", "regally", "clergy", "largely", "leading",
"art", "tar", "rat"
};
string report = string.Join(Environment.NewLine, FindAnagrams(demo)
.Select(group => string.Join(", ", group)));
Console.Write(report);
Outcome:
maters, stream
gallery, largely, regally
art, rat, tar
Edit: Same idea for FindAnagrams_Detects_Anagrams:
public string[] FindAnagrams_Detects_Anagrams(string word, string[] candidates) {
if (word == null || candidates == null)
return new string[0];
string[] wordArr =
string key = string.Concat(word.OrderBy(c => c));
return candidates
.Where(w => w != null)
.Where(w => key == string.Concat(w.OrderBy(c => c)))
.ToArray();
}
You can get rid of Linq if you want:
All anagrams:
public static IEnumerable<string[]> FindAnagrams(IEnumerable<string> candidates) {
if (null == candidates)
throw new ArgumentNullException(nameof(candidates));
Dictionary<string, List<string>> groups =
new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
foreach (var word in candidates) {
char[] keyArray = word.ToCharArray();
Array.Sort(keyArray);
string key = string.Concat(keyArray);
if (groups.TryGetValue(key, out var list))
list.Add(word);
else
groups.Add(key, new List<string>() { word});
}
foreach (var pair in groups) {
if (pair.Value.Count > 1) {
string[] result = new string[pair.Value.Count];
for (int i = 0; i < pair.Value.Count; ++i)
result[i] = pair.Value[i];
yield return result;
}
}
}
Detect anagrams:
public string[] FindAnagrams_Detects_Anagrams(string word, string[] candidates) {
if (word == null || candidates == null)
return new string[0];
char[] keyArray = word.ToCharArray();
Array.Sort(keyArray);
string key = string.Concat(keyArray);
List<string> list = new List<string>();
foreach (string w in candidates) {
char[] wArray = w.ToCharArray();
Array.Sort(wArray);
string wKey = string.Concat(wArray);
if (string.Equals(wKey, key, StringComparison.OrdinalIgnoreCase))
list.Add(w);
}
string[] result = new string[list.Count];
for (int i = 0; i < list.Count; ++i)
result[i] = list[i];
return result;
}

Order alphanumeric string numerically, and then by prefix/suffix

I have a complicated sorting pattern to replicate, and my solution seems a bit hamfisted.
My input is a list of numbers that can have several letters as a suffix and prefix both are only letter ('aaa', 'aab', 'ac' etc).
I need to sort numerically, then sort by suffix (if there is one) and then by prefix (if there is one).
E.g.
"a1a",
"5ac",
"1",
"12",
"2",
"11",
"5aa",
"3",
"5ab",
"a2b",
"abb11ca",
"1b",
"aba11ca"
would be sorted as
1
a1a
1b
2
a2b
3
5aa
5ab
5ac
11
aba11ca
abb11ca
12
Here is the solution that I came up with using Linq.
static void Main(string[] args)
{
var arr = new []
{"b2","a1a","5ac","1","12","2","11","5aa","3","5ab","a1","a2b","abb11ca","1b","aba11ca"
};
var ordered = arr.Select(str => {
var parts = SplitIntoPrefixNumberSuffix(str);
var number = int.Parse(parts[1]);
return new { str, parts, number };
})
.OrderBy(x => x.number).ThenBy(x => x.parts[2]).ThenBy(x => x.parts[0])
.Select(x => x.str);
Console.WriteLine("sorted array: ");
foreach (var s in ordered)
{
Console.WriteLine("{0}", s);
}
Console.ReadLine();
}
public static string[] SplitIntoPrefixNumberSuffix(string str)
{
var numChar = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
var numLoc = str.IndexOfAny(numChar);
var nums = "";
foreach (var c in str)
{
if (char.IsDigit(c))
nums = nums + c;
}
Console.WriteLine("numLoc: {0}; nums: {1}", numLoc, nums.Count());
var prefix = str.Substring(0, numLoc);
var suffix = str.Substring(numLoc + nums.Count());
Console.WriteLine("prefix {0}; nums {1}; suffix {2}", prefix, nums, suffix);
return new[] { prefix, nums, suffix };
}
Here is a .netfiddle of it working: https://dotnetfiddle.net/C7ZA0b.
While it works, it feels like it's not a very good solution. I'm iterating over the collection several times, and I think I should be using a custom comparable.
I've never written a Comparable before; I looked at Dot Net Pearls Alphanumeric Sorting and can follow it but not well enough to modify it to suit my needs.
Is there an IComparable I can use to do the above job? Any suggestions on a good place to learn how to write one?
So you can use regex named groups to split out the various components of the string, then order by each component:
var regex = new Regex(#"^(?<pre>\D*)(?<num>\d+)(?<suff>\D*)$");
var ordered = data.Select(d => (match: regex.Match(d), value: d))
.Where(x => x.match.Success) //throw away anything that doesn't conform
.Select(x => (
x.value,
pre: x.match.Groups["pre"].Value,
num: int.Parse(x.match.Groups["num"].Value),
suff: x.match.Groups["suff"].Value))
.OrderBy(x => x.num)
.ThenBy(x => x.suff)
.ThenBy(x => x.pre)
.Select(x => x.value);
...but ultimately this isn't that different to your solution. I can't really see how a specialized IComparer will simplify this.
If you don't have tuples available ( < C#7.0), swap for anonymous classes:
data.Select(d => new { match = regex.Match(d), value = d})
.Where(x => x.match.Success)
.Select(x => new {
x.value,
pre = x.match.Groups["pre"].Value,
num = int.Parse(x.match.Groups["num"].Value),
suff = x.match.Groups["suff"].Value})
.OrderBy(x => x.num)
.ThenBy(x => x.suff)
.ThenBy(x => x.pre)
.Select(x => x.value)
As option you can implement your own Custom Comparer
public class CustomStringComparer : IComparer<string>
{
public int Compare(string first, string second)
{
var compareByCore = CompareCore(first, second);
var compareBySuffix = CompareSuffix(first, second);
var compareByPrefix = ComparePrefix(first, second);
return compareByCore != 0 ? compareByCore
: compareBySuffix != 0 ? compareBySuffix
: compareByPrefix;
}
private int CompareCore(string a, string b)
{
var firstCoreNumber = Regex.Match(a, #"\d+").Value;
var secondCoreNumber = Regex.Match(b, #"\d+").Value;
if (!string.IsNullOrEmpty(firstCoreNumber) && !string.IsNullOrEmpty(secondCoreNumber))
{
return int.Parse(firstCoreNumber).CompareTo(int.Parse(secondCoreNumber));
}
return 0;
}
private int CompareSuffix(string a, string b)
{
var firstSuffix = Regex.Match(a, #"\D+$").Value;
var secondSuffix = Regex.Match(b, #"\D+$").Value;
return firstSuffix.CompareTo(secondSuffix);
}
private int ComparePrefix(string a, string b)
{
var firstPrefix = Regex.Match(a, #"^\D+").Value;
var secondPrefix = Regex.Match(b, #"^\D+").Value;
return firstPrefix.CompareTo(secondPrefix);
}
}
And when you call order method just send an instance of this comparer:
var arr = new[]
{ "a1a", "5ac", "1", "12", "2", "11", "5aa", "3", "5ab", "a2b", "abb11ca", "1b", "aba11ca" };
var sortedArr = arr.OrderBy(x => x, new CustomStringComparer());
foreach (var s in sortedArr)
{
Console.Write($"{s} ");
}
As an alternative to my existing answer, I wrote a ComparerBuilder<T> helper a while ago that makes some nice client code.
Now you can:
var comparer = new ComparerBuilder<string>()
.SortKey(k => int.Parse(Regex.Match(k, #"\d+").Value))
.ThenKey(k => Regex.Match(k, #"\D*$").Value)
.ThenKey(k => Regex.Match(k, #"^\D*").Value)
.Build();
var ordered = data.OrderBy(x => x, comparer);
Here is the IComparer implementation of alphanumeric by Dave Koelle.
Edit: Adding code sample.
void Main()
{
var arr = new[] {"b2","a1a","5ac","1","12","2","11","5aa","3","5ab","a1","a2b","abb11ca","1b","aba11ca" };
var items = arr.OrderBy(x => x.ToString(), new AlphanumComparator()).ToList();
Console.WriteLine("sorted array: ");
foreach (var s in items)
{
Console.WriteLine("{0}", s);
}
}
public class AlphanumComparator : IComparer<object>
{
private enum ChunkType { Alphanumeric, Numeric };
private bool InChunk(char ch, char otherCh)
{
ChunkType type = ChunkType.Alphanumeric;
if (char.IsDigit(otherCh))
{
type = ChunkType.Numeric;
}
if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
|| (type == ChunkType.Numeric && !char.IsDigit(ch)))
{
return false;
}
return true;
}
public int Compare(object x, object y)
{
String s1 = x as string;
String s2 = y as string;
if (s1 == null || s2 == null)
{
return 0;
}
int thisMarker = 0, thisNumericChunk = 0;
int thatMarker = 0, thatNumericChunk = 0;
while ((thisMarker < s1.Length) || (thatMarker < s2.Length))
{
if (thisMarker >= s1.Length)
{
return -1;
}
else if (thatMarker >= s2.Length)
{
return 1;
}
char thisCh = s1[thisMarker];
char thatCh = s2[thatMarker];
StringBuilder thisChunk = new StringBuilder();
StringBuilder thatChunk = new StringBuilder();
while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || InChunk(thisCh, thisChunk[0])))
{
thisChunk.Append(thisCh);
thisMarker++;
if (thisMarker < s1.Length)
{
thisCh = s1[thisMarker];
}
}
while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || InChunk(thatCh, thatChunk[0])))
{
thatChunk.Append(thatCh);
thatMarker++;
if (thatMarker < s2.Length)
{
thatCh = s2[thatMarker];
}
}
int result = 0;
// If both chunks contain numeric characters, sort them numerically
if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
{
thisNumericChunk = Convert.ToInt32(thisChunk.ToString());
thatNumericChunk = Convert.ToInt32(thatChunk.ToString());
if (thisNumericChunk < thatNumericChunk)
{
result = -1;
}
if (thisNumericChunk > thatNumericChunk)
{
result = 1;
}
}
else
{
result = thisChunk.ToString().CompareTo(thatChunk.ToString());
}
if (result != 0)
{
return result;
}
}
return 0;
}
}

How do I implement words that are not in dictionary must shown with error?

this is a program where a dictionary is implemented with custom regex and it tokenize all every string that is input. Now I want that the strings that does not match with any of the regex must be shown in display with "not in grammar" line. I cannot come across with any type of solution.
static void Main(string[] args)
{
string StringRegex = "\"(?:[^\"\\\\]|\\\\.)*\"";
string IntegerRegex = #"[0-9]+";
string CommentRegex = #"//.*|/\*[\s\S]*\*/";
string KeywordRegex = #"\b(?:astart|ainput|atake|aloop|batcommand|batshow|batprint|batmult|batadd|batsub|batdiv|batif|batelse|batgo|batend|till|and)\b";
string DataTypeRegex = #"\b(?:int|string)\b";
string IdentifierRegex = #"[a-zA-Z]";
string ParenthesisRegex = #"\(|\)";
string BracesRegex = #"\{|\}";
string ArrayBracketRegex = #"\[|\]";
string PuncuationRegex = #"\;|\:|\,|\.";
string RelationalExpressionRegex = #"\>|\<|\==";
string ArthimeticOperatorRegex = #"\+|\-|\*|\/";
string WhitespaceRegex = #" ";
Dictionary<string, string> Regexes = new Dictionary<string, string>()
{
{"String", StringRegex},
{"Integer", IntegerRegex },
{"Comment", CommentRegex},
{"Keyword", KeywordRegex},
{"Datatype", DataTypeRegex },
{"Identifier", IdentifierRegex },
{"Parenthesis", ParenthesisRegex },
{"Brace", BracesRegex },
{"Square Bracket", ArrayBracketRegex },
{"Puncuation Mark", PuncuationRegex },
{"Relational Expression", RelationalExpressionRegex },
{"Arithmetic Operator", ArthimeticOperatorRegex },
{"Whitespace", WhitespaceRegex }
};
string input;
input = Convert.ToString(Console.ReadLine());
var matches = Regexes.SelectMany(a => Regex.Matches(input, a.Value)
.Cast<Match>()
.Select(b =>
new
{
Value = b.Value + "\n",
Index = b.Index,
Token= a.Key
}))
.OrderBy(a => a.Index).ToList();
for (int i = 0; i < matches.Count; i++)
{
if (i + 1 < matches.Count)
{
int firstEndPos = (matches[i].Index + matches[i].Value.Length);
if (firstEndPos > matches[(i + 1)].Index)
{
matches.RemoveAt(i + 1);
i--;
}
}
}
foreach (var match in matches)
{
Console.WriteLine(match);
}
Console.ReadLine();
}
The identifier regex should be changed to
var IdentifierRegex = #"\b[a-zA-Z]\b";
Then, asdasdas will not match and you will be able to test for an empty result, eg.
if (matches.Count == 0)
Console.WriteLine("Not in grammar");
else
{ ... }
See this IDEONE demo.

How to use LINQ to query a subset of string

Suppose I have an unordered List<String> named letters:
letters.Add("d.pdf");
letters.Add("a.pdf");
letters.Add("c.pdf");
letters.Add("b.pdf");
letters.Add("e.pdf");
letters.Add("f.pdf");
I want to order that list alphabetically and then, take the elements between b and d (including the endpoints), so this will return a new list like {"b.pdf","c.pdf","d.pdf"}.
Simple foreach loop solution:
var letters = new List<string> {"d", "a", "c", "b", "e"};
letters.Sort();
var start = "b";
var end = "d";
var r = new List<string>();
foreach (var l in letters)
{
if (l.CompareTo(start) >= 0 && l.CompareTo(end) <= 0)
{
r.Add(l);
}
}
another simple solution:
var letters = new List<string> {"d", "a", "c", "b", "e"};
letters.Sort();
var startIndex = letters.IndexOf(letters.Find(l=> l.CompareTo(start) == 0));
var endIndex = letters.IndexOf(letters.Find(l => l.CompareTo(end) == 0));
var r = letters.GetRange(startIndex, endIndex - startIndex + 1);
If you use TakeWhile, this solution should work, this is more Linq like. The main problem would be to have the starting element in the first index of collection, otherwise TakeWhile won't work.
var letters = new List<string> { "d", "a", "c", "b", "e" };
letters.Sort();
var start = "b";
var end = "d";
var startIndex = letters.IndexOf(letters.Find(l=> l.CompareTo(start) == 0));
var r = letters.GetRange(startIndex, letters.Count - startIndex)
.TakeWhile(l => l.CompareTo(start) >= 0 && l.CompareTo(end) <= 0).ToList();
I'm not sure there is something built-in that would support this easily, but with the addition of a new extension method, you can do it really easily:
void Main()
{
var letters = new List<string>();
letters.Add("d.pdf");
letters.Add("a.pdf");
letters.Add("c.pdf");
letters.Add("b.pdf");
letters.Add("e.pdf");
var results = letters
.OrderBy(x => x)
.SkipWhile(x => x != "b.pdf")
.TakeTo(x => x == "d.pdf")
.ToList();
}
static class Extensions
{
public static IEnumerable<TValue> TakeTo<TValue>(this IEnumerable<TValue> source, Func<TValue, bool> predicate)
{
bool predicateReached = false;
foreach (var value in source)
{
yield return value;
predicateReached = predicate(value);
if (predicateReached) yield break;
}
}
}
The TakeTo extension method works similar to the TakeWhile extension, except it will yield all values until and including the first value which matches the given predicate function.
This should work in your case:
letters.OrderBy(x => x).TakeWhile(x => x != "e.pdf");
If you want to generalize it you can write an extension method:
public static IEnumerable<TSource> GetRange<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> start,
Func<TSource, bool> end)
{
int counter = 0;
foreach (var item in source)
{
if (start(item) || end(item))
{
yield return item;
counter++;
if(counter == 2) yield break;
}else if (counter != 0) yield return item;
}
}
Then use it:
letters.OrderBy(x => x).GetRange(x => x == "b.pdf", x => x == "d.pdf");
Or you can do that without using LINQ, List<T> has also a GetRange method:
letters.Sort();
int startIndex = letters.IndexOf("b.pdf");
int endIndex = letters.IndexOf("d.pdf");
var result = letters.GetRange(startIndex, endIndex - startIndex +1);

Combining arrays of strings together

I'm looking to combine the contents of two string arrays, into a new list that has the contents of both joined together.
string[] days = { "Mon", "Tue", "Wed" };
string[] months = { "Jan", "Feb", "Mar" };
// I want the output to be a list with the contents
// "Mon Jan", "Mon Feb", "Mon Mar", "Tue Jan", "Tue Feb" etc...
How can I do it ? For when it's only two arrays, the following works and is easy enough:
List<string> CombineWords(string[] wordsOne, string[] wordsTwo)
{
var combinedWords = new List<string>();
foreach (var wordOne in wordsOne)
{
foreach (string wordTwo in wordsTwo)
{
combinedWords.Add(wordOne + " " + wordTwo);
}
}
return combinedWords;
}
But I'd like to be able to pass varying numbers of arrays in (i.e. to have a method with the signature below) and have it still work.
List<string> CombineWords(params string[][] arraysOfWords)
{
// what needs to go here ?
}
Or some other solution would be great. If it's possible to do this simply with Linq, even better!
What you want to do is actually a cartesian product of all the arrays of words, then join the words with spaces. Eric Lippert has a simple implementation of a Linq cartesian product here. You can use it to implement CombineWords:
List<string> CombineWords(params string[][] arraysOfWords)
{
return CartesianProduct(arraysOfWords)
.Select(x => string.Join(" ", x))
.ToList();
}
To cross join on any amount of arrays of strings:
// Define other methods and classes here
List<string> CombineWords(params string[][] arraysOfWords)
{
if (arraysOfWords.Length == 0)
return new List<string>();
IEnumerable<string> result = arraysOfWords[0];
foreach( string[] words in arraysOfWords.Skip(1) )
{
var tempWords = words;
result = from r in result
from w in tempWords
select string.Concat(r, " ", w);
}
return result.ToList();
}
Code below works for any number of arrays (and uses linq to some degree):
List<string> CombineWords(params string[][] wordsToCombine)
{
if (wordsToCombine.Length == 0)
return new List<string>();
IEnumerable<string> combinedWords = wordsToCombine[0].ToList();
for (int i = 1; i < wordsToCombine.Length; ++i)
{
var temp = i;
combinedWords = (from x in combinedWords from y in wordsToCombine[temp]
select x + " " + y);
}
return combinedWords.ToList();
}
public static List<string> CombineWords(params string[][] arraysOfWords)
{
var strings = new List<string>();
if (arraysOfWords.Length == 0)
{
return strings;
}
Action<string, int> combineWordsInternal = null;
combineWordsInternal = (baseString, index) =>
{
foreach (var str in arraysOfWords[index])
{
string str2 = baseString + " " + str;
if (index + 1 < arraysOfWords.Length)
{
combineWordsInternal(str2, index + 1);
}
else
{
strings.Add(str2);
}
}
};
combineWordsInternal(string.Empty, 0);
return strings;
}
Second try... I'm not able to do it in LINQ... A little too much complex to linquize correctly :-)
I'm using a local anonymous function (and showing that it's quite complex to recurse on anonymous functions, because you have to declare them separately)
This is a non-recursive solution which buffers strings as it progresses, to reduce the number of concatenations. Therefore it should also be usable for more arrays.
It also preserves your desired order - the items in the first array will always be at the beginning of the resulting string.
var s1 = new string[] { "A", "B", "C" };
var s2 = new string[] { "1", "2", "3", "4" };
var s3 = new string[] { "-", "!", "?" };
var res = Combine(s1, s2, s3);
And the function in question:
private List<string> Combine(params string[][] arrays)
{
if (arrays.Length == 1)
{
// The trivial case - exit.
return new List<string>(arrays[0]);
}
IEnumerable<string> last = arrays[arrays.Length - 1];
// Build from the last array, progress forward
for (int i = arrays.Length - 2; i >= 0; i--)
{
var buffer = new List<string>();
var current = arrays[i];
foreach (var head in current)
{
foreach (var tail in last)
{
// Concatenate with the desired space.
buffer.Add(head + " " + tail);
}
}
last = buffer;
}
return (List<string>)last;
}
Could you try this method ?
static List<string> CombineWords(string[] wordsOne, string[] wordsTwo)
{
var combinedWords = new List<string>();
for(int x = 0; (x <= wordsOne.Length - 1); ++x)
{
for(int y = 0; (x <= wordsTwo.Length - 1); ++y)
{
combinedWords.Add(string.Format("{0} {1}", wordsOne[x], wordsTwo[y]));
}
}
return combinedWords;
}
Kris

Categories

Resources