# String Split a phrase into sentences

Hello this is a Console App(.Net Core) in Visual Studio 2019 and i am trying to split a string into sentences and then add them into a dictionary with Key the first word of the sentence and Value the sentence itself. I have the following code and i wonder if the way i did it has of course a better way to handle it.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace StringParse
{
class Program
{
static void Main(string[] args)
{
string phrase = @"MHD001|LOCCOD|GRCACS|20191117|133601|CB|trx_grcacs_tsk_20191117133601498|
TSA001|X|
TSI001|5637|80337|61335|
TSI001|5841|DE|61313|
MTR001|5|";
/////////////////////////////////////////////////////////////////////
string sentenceSeparator = @"\b[A-Z]{3}\d{3}\b";
string[] words = phrase.Split('|');

StringBuilder sb = new StringBuilder();

for (int i = 0; i < words.Length; i++)
{
//If current word array matches the regex
if (words[i].Equals(sentenceSeparator))
{
// If the current array value doesn't match, then continue the current line.
sb.Append(words[i]);
sb.Append(" ");
}
else
{
if (i == 0)
{
// on the 1st iteration, we don't need a new line before the text value.
sb.Append(words[i]);
sb.Append(" ");
}
else
{
sb.Append(words[i]);
sb.Append(" ");
}
}
}
string[] sentences = sb.ToString().Split('\r');

////////////////////////////////////////////////////////////////////////

Regex regx = new Regex(sentenceSeparator);

MatchCollection matches = Regex.
Matches(phrase, sentenceSeparator,
RegexOptions.Compiled |
RegexOptions.CultureInvariant);

for (int i = 1; i < matches.Count; ++i)
{
phrase = phrase
.Replace(matches[i].Value,
Environment.NewLine + matches[i].Value);
}
///////////////////////////////////////////////////////////////////////////////

Dictionary<Match, string> dic = matches.Zip(sentences, (x, y)
=> new { Key = x, Value = y }).ToDictionary(x => x.Key, y => y.Value);

foreach (KeyValuePair<Match, string> kvp in dic)
{
Console.WriteLine("Key = {0}" + Environment.NewLine + "Value = {1}", kvp.Key, kvp.Value);
}

}

}

}



output will be like that:

Key = MHD001
Value = MHD001 LOCCOD GRCACS 20191117 133601 CB trx_grcacs_tsk_20191117133601498
Key = TSK001
Value = TSK001 GLS GRCACS 59144 59144 2 TU 93000161357 TRACE INF DE 800 please send us the completely address information-thanks 68481 20191117 133555
Key = TSA001
Value = TSA001 X
Key = TSI001
Value = TSI001 3655 please send us the completely address information-thanks 66510
Key = TSI001
Value = TSI001 5637 80337 61335
Key = TSI001
Value = TSI001 5841 DE 61313
Key = MTR001
Value = MTR001 5

• Did you copy the code directly from your IDE? I am asking because in the first loop the whole if..else..if..else seems senseless because in each branch the code is doing the exactly same thing. – Heslacher Jan 2 '20 at 15:09
• Yes i did copy it from the IDE.. first i split the phrase into words. then with stringbuilder and the regex i made it like sentence, then i added to another array. I do not have experience with C# and thats why i wonder if this is ok – Nick Stavrou Jan 2 '20 at 15:11
• This code doesn't seem to work. It creates an error when compiling with Framework 4.8. MatchCollection doesn't contain a Zip method. – tinstaafl Jan 2 '20 at 16:31
• @tinstaafl good catch. Using matches.Cast<Match>().Zip... will succeed without an error, and the dictionary has duplicated keys, thats just strange! – Heslacher Jan 2 '20 at 16:53
• Alright, while I don't have a windows at hand, I was able to verify the code as posted works when running it against dotnet core 3.0.100 when targeting netcoreapp3.0. As such I have reopened the question – Vogel612 Jan 3 '20 at 12:03

The whole if..else..if..else inside the first loop is senseless because each branch does exactly the same.

Using Split('|') and combining the resulting array with one space " " is the same like replacing | by a space.

The Regex regx = new Regex(sentenceSeparator); is superflous because you don't use regex at all.

The loop

for (int i = 1; i < matches.Count; ++i)
{
phrase = phrase
.Replace(matches[i].Value,
Environment.NewLine + matches[i].Value);
}


is superflous because you aren't using phrase afterwards.

As it seems (untested) you can use Zip() with a MatchCollection if you use .NET Core 3.0.100 but to make your code runnable without .NET Core you should change

Dictionary<Match, string> dic = matches.Zip(sentences, (x, y)
=> new { Key = x, Value = y }).ToDictionary(x => x.Key, y => y.Value);


to

Dictionary<Match, string> dic = matches.Cast<Match>().Zip(sentences, (x, y)
=> new { Key = x, Value = y }).ToDictionary(x => x.Key, y => y.Value);


It comes for sure from copying the code (which seems to be posted using Shift + Enter for phrase) from this question to the IDE....if you inspect the result of dict you will see that from the second item on the Value is prepended by a \n. This can be prevented by using TrimStart('\n') on y at new { Key = x, Value = y }.

Implementing the mentioned points will lead to

   static void Main(string[] args)
{

string[] sentences = phrase.Replace('|',' ').Split('\r');
string sentencePattern = @"\b[A-Z]{3}\d{3}\b";

MatchCollection matches = Regex.
Matches(phrase, sentencePattern,
RegexOptions.Compiled |
RegexOptions.CultureInvariant);

Dictionary<Match, string> dic = matches.Cast<Match>().Zip(sentences, (x, y)
=> new { Key = x, Value = y.TrimStart('\n') }).ToDictionary(x => x.Key, y => y.Value);

foreach (KeyValuePair<Match, string> kvp in dic)
{
Console.WriteLine("Key = {0}" + Environment.NewLine + "Value = {1}", kvp.Key, kvp.Value);
}
}

• thank you for your valuable comments and the great review. – Nick Stavrou Jan 4 '20 at 10:45