Related
I have a string like this (including newlines)
A2,
10.22,
-57,
A,
10.23,
-68,
A2,
10.24,
-60,
LB,
10.25,
-62,
I am trying to make this string to look like this:
A2,10.22,-57,
A,10.23,-68,
A2,10.24,-60,
LB,10.25,-62,
I need to join string in every 3 line i have tried :
int numLines = a.Split('\n').Length;
for (int i = 0; i < numLines; i += 3)
{
richTextBox1.Text = a.Replace("\n", "");
}
But it is not working for me. Please help me out
You can also approach this with LINQ, by using the index overload of .Select to retain a running count of the line numbers, and then to group them into groups of 3 - I've used integer division to Floor the line index, 3 at a time, but there are likely other suitable ways.
var groups = values.Select((s, idx) => (Index: idx / 3, Value: s))
.GroupBy(x => x.Index);
Where each item in the groups above will be IEnumerable<(Index, Value)>.
You'll also need to be wary of newlines - these may be \r\n in Windows, not just the \n you've indicated.
Here's an example:
var a =
#"A2,
10.22,
-57,
A,
10.23,
-68,
A2,
10.24,
-60,
LB,
10.25,
-62,";
var values = a.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
var groups = values.Select((s, idx) => (Index: idx / 3, Value: s))
.GroupBy(x => x.Index);
foreach (var grp in groups)
{
Console.WriteLine(string.Join("", grp.Select(x => x.Value)));
}
Since you've already got commas at the end of each string (including the last one), there's no need to add another separator.
Output:
A2,10.22,-57,
A,10.23,-68,
A2,10.24,-60,
LB,10.25,-62,
Why not use the array that the split gives you instead?
var newArr = a.Split('\n');
for (int i = 0; i < newArr.Length; i += 3)
{
richTextBox1.Text = newArr[i] + newArr[i + 1] + newArr[i + 2];
}
Just don't forget to check the length of the arrays so that you don't get a IndexOutOfRange Exception.
I'm assuming that the input is actually coming from a file here.
var file = //file path
var sb = new StringBuilder();
var lineNum = 1;
var output = string.Empty;
using (var reader = new StreamReader(file))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (lineNum % 3 == 0)
{
output += sb.ToString() + "\n";
sb.Clear();
}
else
sb.Append(line);
lineNum++;
}
}
richTextBox1.Text = output;
Try this solution which is a combination of linq and for loop
var result = "";
var items = yourInputString.Split('\n');
for(var i=0; i<items.Count();i=i+3)
{
result += string.Join(",",items.Skip(i).Take(3))+"\n";
}
static void Main(string[] args)
{
var Lines = System.IO.File.ReadAllLines("input.txt");
var Result = new StringBuilder();
var SB = new StringBuilder();
for (var i = 0; i < Lines.Length; i++)
{
SB.Append(Lines[i]);
if ((i+1) % 3 == 0)
{
Result.Append($"{SB.ToString()}{Environment.NewLine}");
SB.Clear();
}
}
System.IO.File.WriteAllText("output.txt", Result.ToString());
}
Try to use Aggregate function
var outPutList = data.Replace("\r", string.Empty).Replace("\n", string.Empty).Split(",").Aggregate(new StringBuilder(""), (x, y) =>
{
if (double.TryParse(y, out double parsedValue))
x.Append(" " + parsedValue);
else
{
x.Append(Environment.NewLine);
x.Append(y.Trim());
}
return x;
});
richTextBox1.Text = outPutList.ToString();
Here is the output
try this works
private void button1_Click(object sender, EventArgs e)
{
//put your string in a textxbox with multiline property set to true
string[] s = textBox1.Text.Replace("\r", "").Replace("\n", "").Split(',');
string r = "";
for (int i = 0; i < s.Length; i++)
{
r = r + s[i] + ",";
if ((i + 1) % 3 == 0)
r = r + "+";
}
if (r.Substring(r.Length - 1, 1) == ",")
r = r.Substring(0, r.Length - 1);
if (r.Substring(r.Length - 1, 1) == "+")
r = r.Substring(0, r.Length - 1);
string[] finalArrayString = r.Trim().Split('+');
//just for show the result
textBox1.Text = "";
for (int i = 0; i < finalArrayString.Length; i++)
textBox1.Text = textBox1.Text + finalArrayString[i] + "\r\n";
}
hope it helps you
This code functions by reading textfile content, manipulate the data in it and display the output in a textbox.
using (StreamReader stRead = new StreamReader(FileUpload1.PostedFile.InputStream))
{
string filenameDate = FileUpload1.FileName.Substring(15, 2);
Dictionary<string, int> dMyobject = new Dictionary<string, int>();
while (!stRead.EndOfStream)
{
var readedLine = stRead.ReadLine();
if (!string.IsNullOrWhiteSpace(readedLine))
{
//int readedLineTime = Convert.ToInt32(readedLine.Substring(09, 02));
string sDate = readedLine.Substring(0, 11);
MatchCollection collection = Regex.Matches(readedLine, #"D;");
countedChars = collection.Count;
if (!dMyobject.Keys.Contains(sDate))
{
dMyobject.Add(sDate, collection.Count);
}
else
{
dMyobject[sDate] = dMyobject[sDate] + collection.Count;
}
}
textfileContent += readedLine + Environment.NewLine;
i++;
}
txtContent.Text = textfileContent;
lblLineCount.Text = i.ToString();
//Label1.Text = this.TextBox1.Text.Split(new Char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Length.ToString();
lblFileDate.Text = filenameDate;
foreach (var item in dMyobject)
{
textfileOutput += (item.Key + " " + item.Value) + Environment.NewLine;
// textfileOutput += (item.Value) + Environment.NewLine;
}
txtOutput.Text = textfileOutput;
}
each line in the textfile contains a date and series of pattern. this code read each lines separated by dates and count the total occurrence of a pattern, in this example, counting the pattern "D;" and summarize its total per Date (lines with same date should be summed-up). For example
running the code will give this current output
2016-12-01 - 7
2016-12-02 - 9
2016-12-03 - 5
2016-12-05 - 1
My Objective and desired output should include in the output the date with NULL VALUE. In my example, there was no 2016-12-04 record, **expected output should have displayed the date still and display 0 or blank such as:**
2016-12-01 - 7
2016-12-02 - 9
2016-12-03 - 5
2016-12-04 - 0
2016-12-05 - 1
This will do it.
using (StreamReader stRead = new StreamReader(#"c:\test.txt"))
{
string filenameDate = "test";
string textfileContent = string.Empty;
int i = 0;
string textfileOutput = string.Empty;
Dictionary<string, int> dMyobject = new Dictionary<string, int>();
while (!stRead.EndOfStream)
{
var readedLine = stRead.ReadLine();
if (!string.IsNullOrWhiteSpace(readedLine))
{
string sDate = readedLine.Substring(0, 11).Trim();
MatchCollection collection = Regex.Matches(readedLine, #"D;");
if (!dMyobject.Keys.Contains(sDate))
{
dMyobject.Add(sDate, collection.Count);
}
else
{
dMyobject[sDate] = dMyobject[sDate] + collection.Count;
}
}
textfileContent += readedLine + Environment.NewLine;
i++;
}
var date = DateTime.Parse(dMyobject.First().Key);
var beginOfMonth = new DateTime(date.Year, date.Month, 1);
var days = new Dictionary<string, int>();
for (var x = 0; x < DateTime.DaysInMonth(date.Year, date.Month); x++)
{
days.Add(beginOfMonth.AddDays(x).ToString("yyyy-MM-dd"), 0);
}
foreach (var item in days)
{
textfileOutput += (dMyobject.ContainsKey(item.Key) ? (item.Key + " " + dMyobject[item.Key]) : (item.Key + " 0")) + Environment.NewLine;
}
}
So the the last bit of code first creates the date ranges that are needed by picking a date from your Dictionary and iterating to the last day of that month. It then checks your matches and counts against the date ranges and if there is a match, use the updated count instead of 0.
In your upload file, there have 2016-12-04 line, you just edit you Regex for got all char and count the total score.
fix 1:Check whether the continuous time.
using (StreamReader stRead = new StreamReader(FileUpload1.PostedFile.InputStream))
{
string filenameDate = FileUpload1.FileName.Substring(15, 2);
SortedDictionary<string, int> dMyobject = new SortedDictionary<string, int>(); //this is a dictionary sorted by key
DateTime? startDatetime = null, endDatetime = null;//got mininum and maxinum dates, at late will be check the continuous time
while (!stRead.EndOfStream)
{
var readedLine = stRead.ReadLine();
if (!string.IsNullOrWhiteSpace(readedLine))
{
string sDate = readedLine.Substring(0, 11).Trim();
DateTime date;
if (DateTime.TryParse(sDate, out date))
{
if (startDatetime.HasValue == false)
startDatetime = date;
endDatetime = date;
//got start date and end date
//if date does not from big to small
//here need compare bwteen date and endDatetime
}
MatchCollection collection = Regex.Matches(readedLine, "(?<c>[A-Z]+);");
if (!dMyobject.Keys.Contains(sDate))
{
dMyobject.Add(sDate, GetTotal(collection));
}
else
{
dMyobject[sDate] = dMyobject[sDate] + GetTotal(collection);
}
}
textfileContent += readedLine + Environment.NewLine;
i++;
}
//here is check the continuous time
if (startDatetime.HasValue && endDatetime.HasValue)
{
for (var dt = startDatetime; dt.Value.CompareTo(endDatetime) <= 0; dt = dt.Value.AddDays(1))
{
string key = dt.Value.ToString("yyyy-MM-dd");
if (!dMyobject.Keys.Contains(key))
{
dMyobject[key] = 0;
}
}
}
txtContent.Text = textfileContent;
lblLineCount.Text = i.ToString();
//Label1.Text = this.TextBox1.Text.Split(new Char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Length.ToString();
lblFileDate.Text = filenameDate;
foreach (var item in dMyobject)
{
textfileOutput += (item.Key + " " + item.Value) + Environment.NewLine;
// textfileOutput += (item.Value) + Environment.NewLine;
}
txtOutput.Text = textfileOutput;
}
//this method is a new method, it got total score, and if you rule chanage you can set `D` +1, `A` +2 etc.
private int GetTotal(MatchCollection collection)
{
Dictionary<string, int> point = new Dictionary<string, int>();
point["D"] = 1;
point["A"] = 0;
int total = 0;
foreach (Match m in collection)
{
string str = m.Groups["c"].Value;
if (point.ContainsKey(str))
total += point[str];
}
return total;
}
Adding comments JohanP Answer, code is working fine expect date sub-string is wrong in original code and there are no issue with Dictionary 0 Value
JohanP's answer is a big help, but there are limitations because it also needs to write the first dates when it is null. So based from his answer, I have come up with this codes.
using (StreamReader stRead = new StreamReader(FileUpload1.PostedFile.InputStream))
{
Dictionary<string, int> dMyobject = new Dictionary<string, int>();
while (!stRead.EndOfStream)
{
var readedLine = stRead.ReadLine();
if (!string.IsNullOrWhiteSpace(readedLine))
{
//int readedLineTime = Convert.ToInt32(readedLine.Substring(11, 02));
string sDate = readedLine.Substring(11, 2);
MatchCollection collection = Regex.Matches(readedLine, #"D;");
countedChars = collection.Count;
if (!dMyobject.Keys.Contains(sDate))
{
dMyobject.Add(sDate, collection.Count);
}
else
{
dMyobject[sDate] = dMyobject[sDate] + collection.Count;
}
}
textfileContent += readedLine + Environment.NewLine;
i++;
}
txtContent.Text = textfileContent;
lblLineCount.Text = i.ToString();
var prevDate = string.Empty;
int tester = 01;
foreach (var item in dMyobject)
{
int testCorrectStart = Convert.ToInt32(item.Key) - tester;
if (testCorrectStart == 0)
{
if (!string.IsNullOrEmpty(prevDate))
{
var cur = Int32.Parse(item.Key); // convert current key into int
var prev = Int32.Parse(prevDate);
int dayDiff = cur - prev;
for (var x = 0; x < dayDiff - 1; x++) // run through day difference, add it to the last date that was added
{
textfileOutput += ((prev + (x + 1)).ToString() + " 0" + Environment.NewLine);
}
}
textfileOutput += (item.Key + " " + item.Value) + Environment.NewLine;
prevDate = item.Key;
tester++;
}
else
{
if (!string.IsNullOrEmpty(tester.ToString()))
{
var cur = Int32.Parse(item.Key); // convert current key into int
var prev = Int32.Parse(tester.ToString());
int dayDiff = cur - prev;
for (var x = 0; x < dayDiff ; x++) // run through day difference, add it to the last date that was added
{
textfileOutput += ("0" +(prev + x).ToString() + " 0" + Environment.NewLine);
}
}
textfileOutput += (item.Key + " " + item.Value) + Environment.NewLine;
prevDate = item.Key;
tester = Convert.ToInt32(prevDate) + 1;
}
}
txtOutput.Text = textfileOutput;
}
you can test its correcteness
I have a []string retry and I have some strings inside of it let's say:
a 2 , a 3 , h 9, asd 123 and so on. They all have intervals between the letter and the integer. Now I need to get how long is the letter part (in this case "qwe 2" the letter length is 3 and the integer part is 2). I'm later using .substring from the specified index at which the letter part finishes and the integer one starts.
string[] s = new string[5];
List<string> retry = new List<string>();
int max = 1;
for (int i = 0; i < s.Length; i++)
{
s[i] = Console.ReadLine();
}
Console.WriteLine(" ");
Array.Sort(s);
for (int j = 0; j < s.Length; j++)
{
if (j > 0)
{
if (s[j] == s[j - 1])
{
max++;
if (retry.Any(x => x.Contains(s[j])))
{
retry.Add(s[j] + " " + max);
}
else
{
if (j <= s.Length - 1)
{
if (s[j-1] != s[j])
{
retry.Add(s[j] + " " + max);
}
else
{
retry.Add(s[j] + " " + max);
}
}
}
}
else
{
max = 1;
}
}
}
for (int j = 0; j < retry.ToArray().Length; j++)
{
for (int k = j + 1; k < retry.ToArray().Length; k++)
{
var a1=retry[j].Substring(0,1);
var a2 = retry[k].Substring(0,1);
if(a1==a2)
{
var b1 = retry[j].Substring(2);
var b2 = retry[k].Substring(2);
if(int.Parse(b1)>int.Parse(b2))
{
retry.Remove(a2 + " "+ b2);
}
else
{
retry.Remove(a1 + " " + b1);
}
}
}
Console.WriteLine(retry[j]);
}
Console.ReadKey();
This only works for 1 letter.
The code below should get the result as expected:
string [] entries = {"xyz 1","q 2","poiuy 4"};
for(int i=0;i<entries.Length;i++)
{
var parts = entries[i].Split(' ');
var txtCount = parts[0].Length;
Console.WriteLine(String.Format("{0} {1}", txtCount, parts[1]));
}
How about...
string[] test = new [] { "qwe 2", "a 2", "b 3", "asd 123" };
foreach (var s in test)
{
var lastLetterIndex = Array.FindLastIndex(s.ToCharArray(), Char.IsLetter);
var lastNumberIndex = Array.FindLastIndex(s.ToCharArray(), Char.IsNumber);
Console.WriteLine(s);
Console.WriteLine("Letter Length : " + (lastLetterIndex + 1));
Console.WriteLine("Number Length : " + (lastNumberIndex - lastLetterIndex));
}
Console.ReadKey();
This iterates through all of the strings and counts the length of the chars, and stores the value of the number, as well as its index. Note that this information is lost upon each iteration, but I'll leave it to you to worry about storing that if you need that information for longer than the duration of an iteration.
int letterLength = 0, number = 0, index = 0;
string[] testString = { "a 2", "a 3", "h 9", "asd 123", "sw", "swa23", "swag 2464" };
foreach (string str in testString)
{
letterLength = 0;
index = -1;
int i = 0;
for (; i < str.Length; i++)
{
char c = str[i];
if (Char.IsLetter(c)) letterLength++;
else if (Char.IsNumber(c)) break;
}
StringBuilder strBuilder = new StringBuilder();
for (; i < str.Length; i++)
{
char c = str[i];
if (index == -1) index = str.IndexOf(c);
strBuilder.Append(c);
number = Int32.Parse(strBuilder.ToString());
}
Console.WriteLine($"String: {str}\nLetter Length: {letterLength} Number: {number} Index: {index}\n");
}
I have a string which are combination of rules which I have to extract into array. AND condition are seperated by commas while OR move to next array index below are some condtion I am trying
(1 AND 2) AND (3 AND 4) => ["1,2,3,4"]
1 OR 2 => ["1","2"]
(1 OR (2 AND 3)) AND 4 => ["1,4","2,3,4"]
(1 OR 2) OR (3 OR 4) => ["1","2","3","4"]
I have tried the below approach is there a better approach than this.
static void Main(string[] args)
{
string input = "4 AND (1 OR (2 AND 3))";
List<string> output = new List<string>();
string inputTmp = input.Replace("(", "")
.Replace(")", "")
.Replace(" AND ", ",");
if (inputTmp.Contains("OR"))
{
List<string> orOutput = new List<string>();
List<List<string>> cmbOutput = new List<List<string>>();
char splitChar = ' ';
if (input.Contains(")) AND ((")) { inputTmp = input.Replace(")) AND ((", "&"); splitChar = '&'; }
else { if (input.Contains(")) AND ")) { inputTmp = input.Replace(")) AND ", "&"); splitChar = '&'; } }
if (input.Contains(")) OR ((")) { inputTmp = input.Replace(")) OR ((", "|"); splitChar = '|'; }
else { if (input.Contains(")) OR ")) { inputTmp = input.Replace(")) OR ", "|"); splitChar = '|'; } }
if (splitChar != ' ')
{
foreach (var item in inputTmp.Split(splitChar))
{
orOutput.Add(item.Replace("(", "").Replace(")", "").Replace(" AND ", ","));
}
}
else
{
orOutput.Add(input.Replace("(", "").Replace(")", "").Replace(" AND ", ","));
}
foreach (var item in orOutput)
{
List<string> lcOutput = new List<string>();
foreach (var oritem in item.Replace(" OR ", "|").Split('|'))
{
lcOutput.Add(oritem);
}
cmbOutput.Add(lcOutput);
}
if (cmbOutput.Count > 1 && splitChar == '&')
{
for (int i = 0; i < cmbOutput[0].Count; i++)
{
for (int j = 0; j < cmbOutput[1].Count; j++)
{
output.Add(cmbOutput[0][i] + "," + cmbOutput[1][j]);
}
}
}
else
{
foreach (var item in cmbOutput)
{
foreach (var initem in item) { output.Add(initem); }
}
}
}
else
{
output.Add(inputTmp);
}
output.ForEach(o => { Console.WriteLine(o); });
Console.ReadLine();
}
I've created a method that I hope meets your needs. It parses the given expression recursively.
Source code
public static string Parse(string s)
{
return '"' + InnerParse(s).Replace(";", "\",\"") + '"';
}
private static string InnerParse(string s)
{
int pos;
while ((pos = s.IndexOf('(')) != -1)
{
int count = 1;
int nextPos = pos;
while (count != 0)
{
nextPos = s.IndexOfAny(new[] { ')', '(' }, nextPos + 1);
if (nextPos == -1 || nextPos >= s.Length)
throw new ApplicationException(); // Unpaired parentheses
count = s[nextPos] == '(' ? count + 1 : count - 1;
}
s = (pos != 0 ? s.Substring(0, pos - 1) : String.Empty)
+ InnerParse(s.Substring(pos + 1, nextPos - pos - 1)) // Recursion
+ s.Substring(nextPos + 1);
}
string[] operands = s.Split(new[] { "AND", "OR" }, StringSplitOptions.None);
if (operands.Length != 2)
throw new ApplicationException(); // Count of operands != 2
string op1 = operands[0].Trim();
string op2 = operands[1].Trim();
// If operator is OR
if (s.Contains("OR"))
return op1 + ';' + op2;
// If operator is AND
string[] op1s = op1.Split(';');
string[] op2s = op2.Split(';');
string[] ret = new string[op1s.Length * op2s.Length];
int i = 0;
foreach (string s1 in op1s)
foreach (string s2 in op2s)
ret[i++] = s1 + ',' + s2;
return String.Join(";", ret);
}
Usage example
Console.WriteLine(Parse("(1 OR (2 AND 3)) AND 4"));
Restrictions
Two operators are recognized: AND, OR.
Operators are case-sensitive.
Operators have exactly two operands.
Operands cannot contain a double-quote " or a semicolon ;.
I am working on c# application and i want to accomplish following task:
I have 12 check boxes for 12 items and user can check any of the check boxes.
if check boxes 3,4,5,6,8,10,11,12 have been checked, I would like to display following output.
You have selected items 3-6,8,10-12.
Rules:
When consecutive number group count is 3 or more than 3,Show grouping like 3-6
else show individual number. 8
Firstly I suggest you to append value of all the checkbox in string like you have shown.
Function Calling :
string data = "3,5,6,7,8,10,12";
string res = GetResultString(data);
Functions :
string GetResultString(string data)
{
string[] arrData = data.Split(',').ToArray();
List<int> lstData = new List<int>();
foreach (string item in arrData)
{
lstData.Add(Convert.ToInt16(item));
}
lstData.Sort();
string finalStr = string.Empty;
if (lstData.Count > 0)
{
int start = lstData[0];
int end = start;
finalStr = string.Empty;
for (int index = 1; index < lstData.Count; index++)
{
if (end + 1 == lstData[index])
{
end = lstData[index];
}
else
{
finalStr += appendResult(start, end);
start = -1;
}
if (start == -1)
{
start = lstData[index];
end = lstData[index];
}
}
finalStr += appendResult(start, end);
}
finalStr = finalStr.Trim(',');
return finalStr;
}
string appendResult(int start,int end)
{
string res = string.Empty;
if (end - start > 1)
{
res += start + "-" + end.ToString() + ",";
start = -1;
}
else
{
while (start <= end)
{
res += start.ToString() + ",";
start++;
}
}
return res;
}
Hope this will done your job,
try this .. it will work i tested it
I have not created checkboxes so it is up to you to check which checkbox is selected get the string like from the selected checkboxes 3,4,5,6,8,10,11,12
string str1 = "3,4,5,6,8,10,11,12";
string[] strArr = str1.Split(',');
List<string> strFinal = new List<string>();
int[] myInts = Array.ConvertAll(strArr, s => int.Parse(s));
int arrLn = myInts.Length;
Array.Sort(myInts);
int intPrevVal = myInts[0];
int intPrevDiff = myInts[0];
int seriesCount = 1;
strFinal.Add(Convert.ToString(myInts[0]));
for (int j = 1; j < arrLn; j++)
{
int intCurr = myInts[j];
if (intCurr - intPrevVal == 1)
{
seriesCount++;
}
else
{
if (seriesCount >= 3)
{
strFinal[strFinal.Count - 1] = strFinal[strFinal.Count - 1] + "-" + intPrevVal;
seriesCount = 1;
}
else if (seriesCount == 2)
{
strFinal.Add(Convert.ToString(myInts[j - 1]));
seriesCount = 1;
//strFinal.Add(Convert.ToString(myInts[j]));
}
strFinal.Add(Convert.ToString(myInts[j]));
}
intPrevVal = intCurr;
}
if (seriesCount >= 3)
{
strFinal[strFinal.Count - 1] = strFinal[strFinal.Count - 1] + "-" + myInts[arrLn - 1];
}
else if (seriesCount == 2)
{
strFinal.Add(Convert.ToString(myInts[arrLn - 1]));
}
string FinalAns = string.Join(",", strFinal.ToArray());
Response.Write(FinalAns);
I suppose you did your checkbox with array (new...) if not do it maunally...
int min=13;
int max=0;
string s = "";
for (int i = 0; i<12; i++)
{
if (cb[i].checked && i<min)
min = i;
else if (cb[i].checked == false)
if (min != 13)
{
max = i-1;
s = s + min.ToString() + "-" + max.ToString() + " ";
min = 13;
}
}
if (cb[11].checked) s = s + min.ToString() + "-12"; // for case the last one is checked
// s contains your data
(I didn't check it but I think it need to be something like this.)
try this
var data = new List<int> { 3, 4, 5, 6, 8, 10, 11, 12 };
// data.Sort();
var groups = new List<string>();
var startIndex = 0;
for (var i = 1; i < data.Count; i++)
{
if (data[i - 1] == data[i] - 1)
{
continue;
}
groups.Add(startIndex == i - 1
? data[startIndex].ToString()
: data[startIndex] + "-" + data[i - 1] );
startIndex = i;
}
groups.Add(startIndex == data.Count - 1
? data[startIndex].ToString()
: data[startIndex] + "-" + data[data.Count - 1]);
var result = string.Join(",", groups);
version 2
[Fact]
public void Test()
{
var data = new List<int> { 3, 4, 5, 7, 8, 10, 11, 12 };
// data.Sort();
var groups = new List<string>();
var startIndex = 0;
for (var i = 1; i < data.Count; i++)
{
if (data[i - 1] == data[i] - 1)
{
continue;
}
AddToGroups(groups, startIndex, i, data);
startIndex = i;
}
AddToGroups(groups, startIndex, data.Count, data);
var result = string.Join(",", groups);
Assert.Equal("3-5,7,8,10-12", result);
}
private static void AddToGroups(List<string> groups, int startIndex, int actualIndex, List<int> data)
{
switch (actualIndex - startIndex)
{
case 1:
groups.Add(data[startIndex].ToString());
break;
case 2:
groups.Add(data[startIndex].ToString());
groups.Add(data[startIndex + 1].ToString());
break;
default:
groups.Add(data[startIndex] + "-" + data[actualIndex - 1]);
break;
}
}
You might have got the solution,but all the above solutions use string for appending data..You could use StringBuilder for optimized performance.
List<int> selectedCB = new List<int>() { 3, 4, 6, 7, 8, 9, 11, 12 };
string output = GetFormattedOutput(selectedCB);
The code for formatting data..
private string GetFormattedOutput(List<int> selectedCB)
{
// Can be changed if you want to increase
// groupby range
int rangeBy = 3;
int diffBy = 1;
int prevValue = 0;
List<int> tempList = new List<int>();
StringBuilder formattedOutput = new StringBuilder();
foreach (int currentValue in selectedCB)
{
var diff = currentValue - prevValue;
if(tempList.Count != 0 && diff > diffBy)
{
// Add the value in templist to formatted output
// If three are more numbers are in range
// Add the first and last
if (tempList.Count >= rangeBy)
{
formattedOutput.Append(tempList[0].ToString() + "-" +
tempList[tempList.Count - 1].ToString()+",");
}
else
{
AppendText(formattedOutput, tempList);
}
tempList.Clear();
}
tempList.Add(currentValue);
prevValue = currentValue;
}
if (tempList.Count != 0)
{
AppendText(formattedOutput, tempList);
}
formattedOutput.Remove(formattedOutput.Length - 1, 1);
return formattedOutput.ToString();
}
// To append the numbers in the list
string AppendText(StringBuilder output, List<int> tempList)
{
foreach (var temp in tempList)
{
output.Append(temp.ToString() + ",");
}
return output.ToString();
}