C#, Split after 2 ;'s in same line - c#

I'm generating a list of from/to coordinate points, together with the distance/time between those points.
For illustration: the header of the end product looks like this
writer.WriteLine("fromX" + ";" + "fromY" + ";" + "toX" + ";" + "toY" + ";" + "distance" + ";" + "time");
The process of calculating from point to point is as follows:
A -> A
A -> B
A -> C
..
B -> A
B -> B
B -> C
etc
The distance and time are per-calculated and are situated in a separate file. However, each line in this file consists of the distance/time between the same start point and every endpoint, thus for example:
0;0;11289;950;9732;899;9886;725;32893;2195;38010;2478;46188;3330;
The goal is to have the following notation in the end product:
point A;point A;0;0
point A;point B;11289;950
point A;point C;9732;899
etc.
As you can see, I would need to split the distance + time line at every 2nd value.
At the moment, I have the following code:
List<string> locationsList = new List<string>();
using (var reader = new StreamReader(File.OpenRead("locations.csv")))
{
while (reader.Peek() != -1)
locationsList.Add(reader.ReadLine());
}
List<string> distanceTime = new List<string>();
using (var reader = new StreamReader(File.OpenRead("distance.csv")))
{
while (reader.Peek() != -1)
distanceTime.Add(reader.ReadLine());
}
using (var writer = new System.IO.StreamWriter("Output.csv"))
{
writer.WriteLine("fromX" + ";" + "fromY" + ";" + "toX" + ";" + "toY" + "distance" + ";" + "time")
foreach (var fromLine in locationsList)
{
splitFrom = fromLine.Split(';');
fromX = splitFrom[0].Trim();
fromY = splitFrom[1].Trim();
foreach (var toLine in locationsList)
{
splitTo = toLine.Split(';');
toX = splitTo[0].Trim();
toY = splitTo[1].Trim();
writer.WriteLine(fromX + ";" + fromY + ";" + toX + ";" + toY);
}
}
MessageBox.Show("Done");
}
This would have to be expanded with probably a foreach loop that reads a line from the distanceTime-list, splits it, takes every first 2 values and writes them together with the start -and end point.
Problem is that I have no idea how to split after every 2nd value.
Do you have any suggestions?

You don't really need to split on every second ';', you just need a slightly different for loop:
using System;
class Program {
static void Main(string[] args) {
string line = "0;0;11289;950;9732;899;9886;725;32893;2195;38010;2478;46188;3330;";
string[] values = line.Split(';');
char pointName = 'A';
for (int i = 0; i < values.Length - 1; i += 2) {
string endProductLine = string.Format("point A;point {0};{1};{2}", pointName, values[i], values[i + 1]);
Console.WriteLine(endProductLine);
pointName++;
}
}
}

https://msdn.microsoft.com/en-us/library/0w4e0fzs.aspx
Use the % operator:
string coords = "0;0;11289;950;9732;899;9886;725;32893;2195;38010;2478;46188;3330;";
string[] values = coords.Split(';');
for(int val=0; val<values.Length; val++)
{
int coord;
if(val % 2 == 0)
{
//this will give you access to every second value
int.TryParse(values[val], out coord);
Console.WriteLine(coord.ToString());
}
}

Related

ForEach In List isn't targetting all

so I currently have a program that adds an item to a list in a format such as
username,Index it then adds one to the index in this code below however. It is only adding one to the item that has been added most recently.
Console.WriteLine("There are currently: " + AntiSpam.Count);
int Index = 0;
foreach (string s in AntiSpam)
{
Console.WriteLine("Found User: " + s.Split(',')[0]);
AntiSpam[Index] = s.Split(',')[0] + "," + (int.Parse(s.Split(',')[1]) + 1).ToString();
Index++;
}
Basically this returns the data There are currently: 10
Found User: someone. It then goes again for another loop of this code and shows the same result again.
EDIT
I have managed to make my code work by using this code
for (var i = 0; i < AntiSpam.Count; i++)
{
AntiSpam[i] = AntiSpam[i].Split(',')[0] + "," + (int.Parse(AntiSpam[i].Split(',')[1]) + 1).ToString();
Console.WriteLine("Text is {0}", AntiSpam[i]);
}
However if possible I would like to know why this works and the first doesn't
If you're going to be indexing a list, just do for rather than foreach. This avoids the need (and possible confusion) of using a separate variable to keep track of the AntiSpam.IndexOf(s) which is basically what you were trying to do with Index:
Console.WriteLine("There are currently: " + AntiSpam.Count);
int index;
string s;
for(int i=0; i < AntiSpam.Count, i++)
{
string[] parts = AntiSpam[i].Split(',');
username = parts[0];
Console.WriteLine("Found User: " + username);
if (parts.Length > 1)
{
index = int.Parse(parts[1])
AntiSpam[i] = username + "," + (index + 1).ToString();
}
}

Split and combine two different strings

I am trying to create a string and below is the thing which i am trying to achieve:
String first = "Previous.value1 | Previous.value2";
String second = "New.value1| New.value2";
I am trying to create final string like this:
string final ="generate Previous.value1 cross New.value1 ? Previous.value1 cross New.value2";
But problem is when i will have mismatch like below:
String first = "Previous.value1 | Previous.value2";
String second = "New.value1";
Then i want to have like this because i dont have matching value for Previous.Value2 in second variable:
string final ="generate Previous.value1 cross New.value1";
So far i am successfully in generating final string when i have same count of value in both the string.
string final = "generate";
if (first.Split('|').Count() - second.Split('|').Count() == 0)
{
int i = 0;
foreach (var item in first.Split('|').Count())
{
if (i == 0)
final = final + item + " cross " + second.Split('|')[i];
else
final = final + " ? " + item + " cross " + second.Split('|')[index];
i++;
}
}
Try this LINQ query (using the Zip extension method):
var zippedQry = first.Split('|').Zip(second.Split('|'),
(f, s) => f.Trim() + " cross " + s.Trim());
string final = "generate " + String.Join(" ? ", zippedQry.ToArray());
To remain in the style that you use, I would suggest to first split both strings
String first = "Previous.value1 | Previous.value2";
String second = "New.value1 | New.value2";
string final = "generate ";
string[] first_values = first.Split('|');
string[] second_values = second.Split('|');
Then you can just orient yourself on the shortest array. If you have in the second_values not enough corresponding values for the first_values you collect only the corresponding ones. Using a normal for-loop:
// run until the length of the shortest one
for (int i = 0; i < Math.Min(first_values.Length, second_values.Length); i++)
{
// bild the normal cross-version for the first position
string sentence = first_values[i] + " cross " + second_values[i];
// if on first position use normal version otherwise put a ? infront of it
final += i == 0 ? sentence : " ? " + sentence;
}
This loop should handle also string like this:
String first = "Previous.value1 | Previous.value2 | Previous.value3 | Previous.value4";
String second = "New.value1 | New.value2 | New.value3";

How to parse below string in C#?

Please someone to help me to parse these sample string below? I'm having difficulty to split the data and also the data need to add carriage return at the end of every event
sample string:
L,030216,182748,00,FF,I,00,030216,182749,00,FF,I,00,030216,182750,00,FF,I,00
batch of events
expected output:
L,030216,182748,00,FF,I,00 - 1st Event
L,030216,182749,00,FF,I,00 - 2nd Event
L,030216,182750,00,FF,I,00 - 3rd Event
Seems like an easy problem. Something as easy as this should do it:
string line = "L,030216,182748,00,FF,I,00,030216,182749,00,FF,I,00,030216,182750,00,FF,I,00";
string[] array = line.Split(',');
StringBuilder sb = new StringBuilder();
for(int i=0; i<array.Length-1;i+=6)
{
sb.AppendLine(string.Format("{0},{1} - {2} event",array[0],string.Join(",",array.Skip(i+1).Take(6)), "number"));
}
output (sb.ToString()):
L,030216,182748,00,FF,I,00 - number event
L,030216,182749,00,FF,I,00 - number event
L,030216,182750,00,FF,I,00 - number event
All you have to do is work on the function that increments the ordinals (1st, 2nd, etc), but that's easy to get.
This should do the trick, given there are no more L's inside your string, and the comma place is always the sixth starting from the beginning of the batch number.
class Program
{
static void Main(string[] args)
{
String batchOfevents = "L,030216,182748,00,FF,I,00,030216,182749,00,FF,I,00,030216,182750,00,FF,I,00,030216,182751,00,FF,I,00,030216,182752,00,FF,I,00,030216,182753,00,FF,I,00";
// take out the "L," to start processing by finding the index of the correct comma to slice.
batchOfevents = batchOfevents.Substring(2);
String output = "";
int index = 0;
int counter = 0;
while (GetNthIndex(batchOfevents, ',', 6) != -1)
{
counter++;
if (counter == 1){
index = GetNthIndex(batchOfevents, ',', 6);
output += "L, " + batchOfevents.Substring(0, index) + " - 1st event\n";
batchOfevents = batchOfevents.Substring(index + 1);
} else if (counter == 2) {
index = GetNthIndex(batchOfevents, ',', 6);
output += "L, " + batchOfevents.Substring(0, index) + " - 2nd event\n";
batchOfevents = batchOfevents.Substring(index + 1);
}
else if (counter == 3)
{
index = GetNthIndex(batchOfevents, ',', 6);
output += "L, " + batchOfevents.Substring(0, index) + " - 3rd event\n";
batchOfevents = batchOfevents.Substring(index + 1);
} else {
index = GetNthIndex(batchOfevents, ',', 6);
output += "L, " + batchOfevents.Substring(0, index) + " - " + counter + "th event\n";
batchOfevents = batchOfevents.Substring(index + 1);
}
}
output += "L, " + batchOfevents + " - " + (counter+1) + "th event\n";
Console.WriteLine(output);
}
public static int GetNthIndex(string s, char t, int n)
{
int count = 0;
for (int i = 0; i < s.Length; i++)
{
if (s[i] == t)
{
count++;
if (count == n)
{
return i;
}
}
}
return -1;
}
}
Now the output will be in the format you asked for, and the original string has been decomposed.
NOTE: the getNthIndex method was taken from this old post.
If you want to split the string into multiple strings, you need a set of rules,
which are implementable. In your case i would start splitting the complete
string by the given comma , and than go though the elements in a loop.
All the strings in the loop will be appended in a StringBuilder. If your ruleset
say you need a new line, just add it via yourBuilder.Append('\r\n') or use AppendLine.
EDIT
Using this method, you can also easily add new chars like L or at the end rd Event
Look for the start index of 00,FF,I,00 in the entire string.
Extract a sub string starting at 0 and index plus 10 which is the length of the characters in 1.
Loop through it again each time with a new start index where you left of in 2.
Add a new line character each time.
Have a try the following:
string stream = "L,030216,182748,00,FF,I,00, 030216,182749,00,FF,I,00, 030216,182750,00,FF,I,00";
string[] lines = SplitLines(stream, "L", "I", ",");
Here the SplitLines function is implemented to detect variable-length events within the arbitrary-formatted stream:
string stream = "A;030216;182748 ;00;FF;AA;01; 030216;182749;AA;02";
string[] lines = SplitLines(batch, "A", "AA", ";");
Split-rules are:
- all elements of input stream are separated by separator(, for example).
- each event is bounded by the special markers(L and I for example)
- end marker is previous element of event-sequence
static string[] SplitLines(string stream, string startSeq, string endLine, string separator) {
string[] elements = stream.Split(new string[] { separator }, StringSplitOptions.RemoveEmptyEntries);
int pos = 0;
List<string> line = new List<string>();
List<string> lines = new List<string>();
State state = State.SeqStart;
while(pos < elements.Length) {
string current = elements[pos].Trim();
switch(state) {
case State.SeqStart:
if(current == startSeq)
state = State.LineStart;
continue;
case State.LineStart:
if(++pos < elements.Length) {
line.Add(startSeq);
state = State.Line;
}
continue;
case State.Line:
if(current == endLine)
state = State.LineEnd;
else
line.Add(current);
pos++;
continue;
case State.LineEnd:
line.Add(endLine);
line.Add(current);
lines.Add(string.Join(separator, line));
line.Clear();
state = State.LineStart;
continue;
}
}
return lines.ToArray();
}
enum State { SeqStart, LineStart, Line, LineEnd };
f you want to split the string into multiple strings, you need a set of rules, which are implementable. In your case i would start splitting the complete string by the given comma , and than go though the elements in a loop. All the strings in the loop will be appended in a StringBuilder. If your ruleset say you need a new line, just add it via yourBuilder.Append('\r\n') or use AppendLine.

Getting only the last entry on counting

I'm only getting the last entry of the counting typed like this
public string ZodziuSkaiciavimas()
{
foreach (var sentence in Sakiniai.TrimEnd('.').Split('.'))
{
Rezultatas=(eilute.ToString() + " sakinyje zodziu:" + (sentence.Trim().Split(' ').Count() + sentence.Trim().Split('-').Count() + sentence.Trim().Split(';').Count() + sentence.Trim().Split(':').Count() + sentence.Trim().Split(',').Count() - 4));
eilute++;
}
return Rezultatas;
And I need to get the answer with a return type.
If I type code like this than i get what i want,but no returns.
public string ZodziuSkaiciavimas()
{
foreach (var sentence in Sakiniai.TrimEnd('.').Split('.'))
{
Console.WriteLine(eilute.ToString() + " sakinyje zodziu:" + (sentence.Trim().Split(' ').Count() + sentence.Trim().Split('-').Count() + sentence.Trim().Split(';').Count() + sentence.Trim().Split(':').Count() + sentence.Trim().Split(',').Count() - 4));
eilute++;
}
return Rezultatas;
}
Why arent you appending your results as below
Rezultatas +=(eilute.ToString() + " sakinyje zodziu:" + (sentence.Trim().Split(' ').Count() + sentence.Trim().Split('-').Count() + sentence.Trim().Split(';').Count() + sentence.Trim().Split(':').Count() + sentence.Trim().Split(',').Count() - 4)) + "\n";
It looks like you want to return multiple numbers from your method, but Rezultatas is a single string. You can fix it by changing the return type to List<int>, and returning a list:
public List<int> ZodziuSkaiciavimas() {
var Rezultatas = new List<int>()
foreach (var sentence in Sakiniai.TrimEnd('.').Split('.')) {
var res = sentence.Trim().Split(' ', '-', ';', ':', ',').Length;
Rezultatas.Add(res);
}
return Rezultatas;
}
When the callers decide to print the Rezultatas they gets back from your method, they could decide what character to put between the numbers (say, a comma ',') and print it like this:
var numbers = ZodziuSkaiciavimas();
Console.WriteLine(string.Join(", ", numbers));

Data lost while adding string to listbox

I am cycling through the contents of a two-dimensional array containing the result of a Punnett Square calculation for gene crosses. I need to summarize the result so that the user can readily see the unique instances. I can accomplish this by putting the result into a text box, but when I try and use a ListBox to display the data, part of the information is getting lost, namely a translation of the AaBBCc type data to something that directly relates to the traits that the user initially selected.
This is the main block of code for the operation:
foreach (string strCombination in arrUniqueCombinations)
{
int intUniqueCount = 0;
decimal decPercentage;
foreach (string strCrossResult in arrPunnettSQ)
{
if (strCrossResult == strCombination)
{
intUniqueCount++;
}
}
decPercentage = Convert.ToDecimal((intUniqueCount*100)) / Convert.ToDecimal(intPossibleCombinations);
txtReport.AppendText(strCombination + " appears " + intUniqueCount.ToString() + " times or " + decPercentage.ToString() + "%."+ Environment.NewLine);
lstCrossResult.Items.Add(DecodeGenome(strCombination) + " appears " + intUniqueCount.ToString() + " times or " + decPercentage.ToString() + "%.");
}
For appending the data to the textbox I use this code and it works perfectly:
txtReport.AppendText(DecodeGenome(strCombination) + " appears " + intUniqueCount.ToString() + " times or " + decPercentage.ToString() + "%."+ Environment.NewLine);
Giving the result:
Trait 1 Het.,Trait 3 appears 16 times or 25%.
For adding the result to a list box, this works:
lstCrossResult.Items.Add(strCombination + " appears " + intUniqueCount.ToString() + " times or " + decPercentage.ToString() + "%.");
Giving the result:
AaBBCc appears 16 times or 25%.
But the contents of strCombination is AaBBCc and I need it translated to "Trait 1 Het.,Trait 3", which I accomplish with this bit of code:
private string DecodeGenome(string strGenome)
{
string strTranslation = "";
int intLength = strGenome.Length;
int intCounter = intLength / 2;
string[] arrPairs = new string[intLength / 2];
//Break out trait pairs and load into array
for (int i = 1; i <= intLength; i++)
{
arrPairs[i / 2] = strGenome.Substring((i-1),2);
i++;
}
foreach (string strPair in arrPairs)
{
char chFirstLetter = strPair[0];
char chSecondLetter = strPair[1];
intCounter = intCounter - 1;
if (Char.IsUpper(chFirstLetter))
{
if (!Char.IsUpper(chSecondLetter))
{
if (intCounter > 0)
{
txtReport.AppendText(GetDescription(strPair.Substring(0, 1)) + " Het.,");
}
else
{
txtReport.AppendText(GetDescription(strPair.Substring(0, 1)));
}
}
}
else
{
if (!Char.IsUpper(chSecondLetter))
{
if (intCounter > 0)
{
txtReport.AppendText(GetDescription(strPair.Substring(0, 1)) + ",");
}
else
{
txtReport.AppendText(GetDescription(strPair.Substring(0, 1)));
}
}
}
}
return strTranslation;
}
That has no problem displaying in a text box, but when I try and put it as an item into a list box it turns it into null. Instead of:
"Trait 1 Het.,Trait 3 appears 16 times or 25%."
I get:
" appears 16 times or 25%."
I have tried adding the results to an ArrayList, then populating the listbox after everything is processed, but the result is the same.
Any clues as to why the list box is not accepting the translated AaBBCc information would be greatly appreciated.
strTranslation is never set. Everything is pushed to txtReport.AppendText

Categories

Resources