Delimit string and put it in listbox - c#

I have a string like:
one,one,one,one,two,two,two,two,three,three,three,three,four,four,four,four,...
and I'd like to delimit it after every fourth comma and store it into a list box, like this:
one,one,one,one,
two,two,two,two,
three,three,three,three,
four,four,four,four,
...
What should be appropriate way to do this? Should I supposed to use regex to somehow delimit this string?
Thanks

Linqless alternative;
int s = 0, n = 0, len = inputString.Length;
for (var i = 0; i < len; i++) {
if (inputString[i] == ',' && ++n % 4 == 0 || i == len - 1) {
aListBox.Items.Add(inputString.Substring(s, i - s + 1));
s = i + 1;
}
}

This LINQ breaks your input into individual strings by delimiting on the comma, then uses an index in the Select method to group four items together at a time, then finally joins those four items into a single string again.
var input = "one,one,one,one,two,two,two,two,three,three,three,three"; // and so on
var result = input.Split(',')
.Select((s, i) => new {s, i})
.GroupBy(pair => pair.i / 4)
.Select(grp => string.Join(",", grp.Select(pair => pair.s)) + ",");
The result is a collection of strings, where the first one (based on your input) is "one,one,one,one,", then the second is "two,two,two,two," and so on...
From there, it's just a matter of setting it as the DataSource, ItemsSource or similar, depending on what technology you're using.

Related

How to remove empty strings from c# array?

I am removing values with less than 8 characters from an array, but empty strings still remain. How to get rid of them?
for (int i = 0; i < reportbOR.Length; i++)
{
border = "border:" +reportbOR[i].colorborder;
string[] text_arr = border.Split('\n');
foreach (var item in text_arr)
{
if (item.Length < 8)
border = border.Replace(item, "");
}
}
You could filter out null, white space, and character length with a couple of different approaches. We will start similar to your above code.
var border = new List<string>();
for(var index = 0; index < reportbOR.Length; index++)
if(!String.IsNullOrEmpty(reportbOR[index].colorBorder) && reportbOR[index].colorBorder.Length < 8)
border.Add($"border: {reportbOR[index].colorBorder}");
I won't mention the StringSplitOptions because of #NSevens response, the documentation is pretty self explanatory but could be applicable.
One thing to denote, if you use LINQ on a String it will parse into a character array. But if you use your root object you could leverage LINQ.
var borders = reportbOR
.Where(property => !String.IsNullOrEmpty(property.colorBorder) && property.colorBorder < 8))
.Select(property => new $"border: {property}");
Also, it would be important to know that if you only care about null or empty, then use IsNullOrEmpty. If you want to sanitize for white-space and white-space characters then use IsNullOrWhiteSpace.
You can use the overload of string.Split which accepts a StringSplitOptions enum:
string[] text_arr = border.Split(new string[] { "\n" },
StringSplitOptions.RemoveEmptyEntries);
Since it's unclear as to when you are having issues removing the empty entries, and you are also replacing strings with 8 or less characters with empty strings, you can use Linq:
text_arr = text_arr.Where(a => !string.IsNullOrWhitespace(a)).ToArray();
You can read about the overload here: stringsplitoptions
And here: string.split
EDIT Here it is in one line:
string[] text_arr = border.Split(new string[] { "\n" },
StringSplitOptions.RemoveEmptyEntries)
.Where(a => !string.IsNullOrWhitespace(a) && a.Length >= 8)
.ToArray();
Something like this?
for (int i = 0; i < reportbOR.Length; i++)
{
var strings = $"border:{reportbOR[i].colorborder}"
.Split(Environment.NewLine)
.Where(str => str.Length > 8);
border = string.Join(Environment.NewLine, strings);
}
try this
list = list.Where(s => !string.IsNullOrWhiteSpace(s)).ToList();

How can I split string into array of string that take two characters with including the previous last character?

I have this word and I would like to split it into arrays with taking the last previous character in every iteration of split .
string word = "HelloWorld!";
string[] mystringarray = word.Select(x => new string(x, 2)).ToArray();
Console.WriteLine(mystringarray);
Output result :
[HH,ee,ll,oo,WW,oo,rr,ll,dd,!!]
Expected result :
[He,el,ll,lo,ow,wo,or,rl,ld]
How can I achieve that?
If you want a LINQ solution, you could use Zip:
string[] mystringarray = word
.Zip(word.Skip(1), (a, b) => $"{a}{b}")
.ToArray();
This zips each character in word with itself, using Skip as an offset, and still has O(n) complexity as with an explicit loop.
string word = "HelloWorld!";
List<string> mystringarray = new();
for (int i = 1; i < word.Length; i++)
{
mystringarray.Add(word.Substring(i-1, 2));
}
Contents of mystringarray:
List<string>(10) { "He", "el", "ll", "lo", "oW", "Wo", "or", "rl", "ld", "d!" }
Note the exclamation mark which I'm not sure you wanted to include.
I think the following code implements the output you want.
Its algorithm is :
(0, 1),
(1, 2),
(2, 3),
......
string[] mystringarray = word.
Select((x,i) =>
i == 0 || i + 1 == word.Length ? x.ToString() : x + word.Substring(i,1))
.ToArray();
Or use Enumerable.Range
string[] mystringarray2 = Enumerable.Range(0, word.Length).
Select(i => i == 0 || i + 1 == word.Length ? word[i].ToString() : word[i] + word.Substring(i,1))
.ToArray();

Join List of strings trailing values don't require delimiters if the right most value is null

What im trying to achieve, but cant get my head around is if i have a list of strings say:
{"test","","data","","123","44"}
this should be joined by a character:
test::data::123:44
but if the list at the end is empty dont delimiter it
{"test","","data","","",""}
should be:
test::data
{"test","","","","",""}
should be:
test
{"test","","","","","44"}
should be:
test::::44
however the list can be of varying lengths which adds another level of complexity.
Just exclude the trailing empty elements from the list Count and then Join the remaining using Take:
List<string> input = ...;
int count = input.Count;
while (count > 0 && string.IsNullOrEmpty(input[count - 1]))
count--;
var output = string.Join(":", input.Take(count));
Using the List<T> specific FindLastIndex method, it can be reduced to the following "one liner":
var output = string.Join(":",
input.Take(input.FindLastIndex(s => !string.IsNullOrEmpty(s)) + 1));
First, with your array:
test = test.Where(x => !string.IsNullOrEmpty(x)).ToArray();
where "test" is your array.
then:
string.Join("::", test);
EDIT:
If you're getting your array of strings by splitting another string, consider the following:
string[] strs = myString.split(someDelimeter, StringSplitOptions.RemoveEmptyEntries);
Start by identifying the last element you want, then slice your list and join as you normally would:
var lastElementIndex = strings.Select((s, index) => string.IsNullOrEmpty(s) ? -1 : index).Max();
var prefix = strings.Take(lastElementIndex + 1);
var result = string.Join(":", prefix);
var obj = {"test","","data","","123","44"};
var count = obj.Count;
for (var i = count - 1; i > -1; i--)
{
if (obj[i]==String.Empty) {
obj.RemoveAt(i);
}
else break;
}
var arr = obj.Split(new char[] { ','}, StringSplitOptions.RemoveEmptyEntries);
var output = arr.Join(":", arr);

Does Linq provide a way to easily spot gaps in a sequence?

I am managing a directory of files. Each file will be named similarly to Image_000000.png, with the numeric portion being incremented for each file that is stored.
Files can also be deleted, leaving gaps in the number sequence. The reason I am asking is because I recognize that at some point in the future, the user could use up the number sequence unless I takes steps to reuse numbers when they become available. I realize that it is a million, and that's a lot, but we have 20-plus year users, so "someday" is not out of the question.
So, I am specifically asking whether or not there exists a way to easily determine the gaps in the sequence without simply looping. I realize that because it's a fixed range, I could simply loop over the expected range.
And I will unless there is a better/cleaner/easier/faster alternative. If so, I'd like to know about it.
This method is called to obtain the next available file name:
public static String GetNextImageFileName()
{
String retFile = null;
DirectoryInfo di = new DirectoryInfo(userVars.ImageDirectory);
FileInfo[] fia = di.GetFiles("*.*", SearchOption.TopDirectoryOnly);
String lastFile = fia.Where(i => i.Name.StartsWith("Image_") && i.Name.Substring(6, 6).ContainsOnlyDigits()).OrderBy(i => i.Name).Last().Name;
if (!String.IsNullOrEmpty(lastFile))
{
Int32 num;
String strNum = lastFile.Substring(6, 6);
String strExt = lastFile.Substring(13);
if (!String.IsNullOrEmpty(strNum) &&
!String.IsNullOrEmpty(strExt) &&
strNum.ContainsOnlyDigits() &&
Int32.TryParse(strNum, out num))
{
num++;
retFile = String.Format("Image_{0:D6}.{1}", num, strExt);
while (num <= 999999 && File.Exists(retFile))
{
num++;
retFile = String.Format("Image_{0:D6}.{1}", num, strExt);
}
}
}
return retFile;
}
EDIT: in case it helps anyone, here is the final method, incorporating Daniel Hilgarth's answer:
public static String GetNextImageFileName()
{
DirectoryInfo di = new DirectoryInfo(userVars.ImageDirectory);
FileInfo[] fia = di.GetFiles("Image_*.*", SearchOption.TopDirectoryOnly);
List<Int32> fileNums = new List<Int32>();
foreach (FileInfo fi in fia)
{
Int32 i;
if (Int32.TryParse(fi.Name.Substring(6, 6), out i))
fileNums.Add(i);
}
var result = fileNums.Select((x, i) => new { Index = i, Value = x })
.Where(x => x.Index != x.Value)
.Select(x => (Int32?)x.Index)
.FirstOrDefault();
Int32 index;
if (result == null)
index = fileNums.Count - 1;
else
index = result.Value - 1;
var nextNumber = fileNums[index] + 1;
if (nextNumber >= 0 && nextNumber <= 999999)
return String.Format("Image_{0:D6}", result.Value);
return null;
}
A very simple approach to find the first number of the first gap would be the following:
int[] existingNumbers = /* extract all numbers from all filenames and order them */
var allNumbers = Enumerable.Range(0, 1000000);
var result = allNumbers.Where(x => !existingNumbers.Contains(x)).First();
This will return 1,000,000 if all numbers have been used and no gaps exist.
This approach has the drawback that it performs rather badly, as it iterates existingNumbers multiple times.
A somewhat better approach would be to use Zip:
allNumbers.Zip(existingNumbers, (a, e) => new { Number = a, ExistingNumber = e })
.Where(x => x.Number != x.ExistingNumber)
.Select(x => x.Number)
.First();
An improved version of DuckMaestro's answer that actually returns the first value of the first gap - and not the first value after the first gap - would look like this:
var tmp = existingNumbers.Select((x, i) => new { Index = i, Value = x })
.Where(x => x.Index != x.Value)
.Select(x => (int?)x.Index)
.FirstOrDefault();
int index;
if(tmp == null)
index = existingNumbers.Length - 1;
else
index = tmp.Value - 1;
var nextNumber = existingNumbers[index] + 1;
Improving over the other answer, use the alternate version of Where.
int[] existingNumbers = ...
var result = existingNumbers.Where( (x,i) => x != i ).FirstOrDefault();
The value i is a counter starting at 0.
This version of where is supported in .NET 3.5 (http://msdn.microsoft.com/en-us/library/bb549418(v=vs.90).aspx).
var firstnonexistingfile = Enumerable.Range(0,999999).Select(x => String.Format("Image_{0:D6}.{1}", x, strExt)).FirstOrDefault(x => !File.Exists(x));
This will iterate from 0 to 999999, then output the result of the String.Format() as an IEnumerable<string> and then find the first string out of that sequence that returns false for File.Exists().
It's an old question, but it has been suggested (in the comments) that you could use .Except() instead. I tend to like this solution a little better since it will give you the first missing number (the gap) or the next smallest number in the sequence. Here's an example:
var allNumbers = Enumerable.Range(0, 999999); //999999 is arbitrary. You could use int.MaxValue, but it would degrade performance
var existingNumbers = new int[] { 0, 1, 2, 4, 5, 6 };
int result;
var missingNumbers = allNumbers.Except(existingNumbers);
if (missingNumbers.Any())
result = missingNumbers.First();
else //no missing numbers -- you've reached the max
result = -1;
Running the above code would set result to:
3
Additionally, if you changed existingNumbers to:
var existingNumbers = new int[] { 0, 1, 3, 2, 4, 5, 6 };
So there isn't a gap, you would get 7 back.
Anyway, that's why I prefer Except over the Zip solution -- just my two cents.
Thanks!

Splitting an string into a string array.?

I am facing a problem while executing a sql query in C#.The sql query throws an error when the string contains more than 1000 enteries in the IN CLAUSE .The string has more than 1000 substrings each seperated by ','.
I want to split the string into string array each containing 999 strings seperated by ','.
or
How can i find the nth occurence of ',' in a string.
Pull the string from SQL server into a DataSet using a utilities code like
string strResult = String.Empty;
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = strSQL;
strResult = cmd.ExecuteScalar().ToString();
}
Get the returned string from SQL Server
Split the string on the ','
string[] strResultArr = strResult.Split(',');
then to get the nth string that is seperated by ',' (I think this is what you mean by "How can i find the nth occurence of ',' in a string." use
int n = someInt;
string nthEntry = strResultArr[someInt - 1];
I hope this helps.
You could use a regular expression and the Index property of the Match class:
// Long string of 2000 elements, seperated by ','
var s = String.Join(",", Enumerable.Range(0,2000).Select (e => e.ToString()));
// find all ',' and use '.Index' property to find the position in the string
// to find the first occurence, n has to be 0, etc. etc.
var nth_position = Regex.Matches(s, ",")[n].Index;
To create an array of strings of your requiered size, you could split your string and use LINQ's GroupBy to partition the result, and then joining the resulting groups together:
var result = s.Split(',').Select((x, i) => new {Group = i/1000, Value = x})
.GroupBy(item => item.Group, g => g.Value)
.Select(g => String.Join(",", g));
result now contains two strings, each with 1000 comma seperated elements.
How's this:
int groupSize = 1000;
string[] parts = s.Split(',');
int numGroups = parts.Length / groupSize + (parts.Length % groupSize != 0 ? 1 : 0);
List<string[]> Groups = new List<string[]>();
for (int i = 0; i < numGroups; i++)
{
Groups.Add(parts.Skip(i * groupSize).Take(groupSize).ToArray());
}
Maybe something like this:
string line = "1,2,3,4";
var splitted = line.Split(new[] {','}).Select((x, i) => new {
Element = x,
Index = i
})
.GroupBy(x => x.Index / 1000)
.Select(x => x.Select(y => y.Element).ToList())
.ToList();
After this you should just String.Join each IList<string>.
//initial string of 10000 entries divided by commas
string s = string.Join(", ", Enumerable.Range(0, 10000));
//an array of entries, from the original string
var ss = s.Split(',');
//auxiliary index
int index = 0;
//divide into groups by 1000 entries
var words = ss.GroupBy(w =>
{
try
{
return index / 1000;
}
finally
{
++index;
}
})//join groups into "words"
.Select(g => string.Join(",", g));
//print each word
foreach (var word in words)
Console.WriteLine(word);
Or you may find the indeces in the string and split it into substrings afterwards:
string s = string.Join(", ", Enumerable.Range(0, 100));
int index = 0;
var indeces =
Enumerable.Range(0, s.Length - 1).Where(i =>
{
if (s[i] == ',')
{
if (index < 9)
++index;
else
{
index = 0;
return true;
}
}
return false;
}).ToList();
Console.WriteLine(s.Substring(0, indeces[0]));
for (int i = 0; i < indeces.Count - 1; i++)
{
Console.WriteLine(s.Substring(indeces[i], indeces[i + 1] - indeces[i]));
}
However, I would think over, if it was possible to work with the entries before they are combined into one string. And probably think, if it was possible to prevent the necessity to make a query which needs that great list to pass into the IN statement.
string foo = "a,b,c";
string [] foos = foo.Split(new char [] {','});
foreach(var item in foos)
{
Console.WriteLine(item);
}

Categories