My question is, if I have a list that looks something like the following,
var list = new List<string>();
list.Add("12345");
list.Add("Words");
list.Add("Are");
list.Add("Here");
list.Add("13264");
list.Add("More");
list.Add("Words");
list.Add("15654");
list.Add("Extra");
list.Add("Words");
And I want to be able to delete all the strings that start with numbers from the list and also concatenate the strings in between them so that it looks like the following,
Words Are Here
More Words
Extra Words
How does that logic look? Below is what I have been trying to do, but I can't first out how to delete the strings with number much less create a newline when i delete a string with numbers.
foreach (string s in list)
{
if (s.StartsWith("1"))
s.Remove(0, s.Length);
else
String.Concat(s);
}
foreach (string p in list)
Console.WriteLine(p);
CodePudding user response:
What you're doing is "chunking" or "paging" your data, but you need to walk through each source element one by one to determine where your pages start and stop.
public static IEnumerable<ICollection<T>> ChunkBy<T>(IEnumerable<T> source, Func<T, bool> predicate)
{
ICollection<T> currentChunk = new List<T>();
foreach (var item in source)
{
if (predicate(item))
{
if (currentChunk.Any())
{
yield return currentChunk;
currentChunk = new List<T>();
}
}
else
{
currentChunk.Add(item);
}
}
if (currentChunk.Any())
{
yield return currentChunk;
}
}
This is a reusable method (and you could add this
to the start of the first parameter to make it an extension method) that takes advantage of IEnumerable and yield-ing results. In essence, you return a stream of values, where each value is a collection. So it's a list of a list, but with much more fluid terms.
What this does is
- Start with an empty temporary list for the current "page" of items.
- Loop through each element in your source.
- For each element, decide if it's the start of a new page or not.
- If it is a new block, return what you've saved up for the current chuck, assuming that saved chunk is not empty.
- If it is not a new block, add the entry to the current chunk.
- When you're done going through all the entries, send the final chunk, assuming it's not empty.
You can call this method like this:
var output = ChunkBy(list, x => char.IsNumber(x[0]))
.Select(ch => string.Join(" ", ch));
foreach (var o in output)
Console.WriteLine(o);
Which gives
Words Are Here
More Words
Extra Words
CodePudding user response:
You could try something like:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var list = new List<string>();
list.Add("12345");
list.Add("Words");
list.Add("Are");
list.Add("Here");
list.Add("13264");
list.Add("More");
list.Add("Words");
list.Add("15654");
list.Add("Extra");
list.Add("Words");
var resultStrings = new List<string>();
string currentString = "";
foreach (string s in list)
{
if (s.StartsWith("1"))
{
resultStrings.Add(currentString);
currentString = "";
}
else
{
currentString = s " ";
}
}
resultStrings.Add(currentString);
foreach (string p in resultStrings)
Console.WriteLine(p);
}
}
basically I am looping thou the list if the value is not starting with 1 I add the value to the currentString
.
When the value does start with 1 we add the currentString
to the resultStrings
an start a new currentString
.
There are some improvements possible, the if with starts withs 1 is not completely fool prove.
You could use int.Parse
.
Is multiple items in a row start with 1 you can end up with empty string in the resultStrings
You could fix this bij checking is the string is empty before adding it to the list
CodePudding user response:
Another approach
var list = new List<string>();
list.Add("12345");
list.Add("Words");
list.Add("Are");
list.Add("Here");
list.Add("13264");
list.Add("More");
list.Add("Words");
list.Add("15654");
list.Add("Extra");
list.Add("Words");
var lines = new List<KeyValuePair<int, string>>();
var currentIndex = 0;
foreach (var line in list.Where(x => x.Length > 0))
{
var firstChar = line.Substring(0, 1)
.ToCharArray()
.First();
if (char.IsNumber(firstChar))
{
currentIndex ;
continue;
}
lines.Add(new KeyValuePair<int, string>(currentIndex, line));
}
foreach (var lineGroup in lines.GroupBy(x => x.Key))
{
Console.WriteLine(string.Join(" ", lineGroup.Select(x => x.Value)));
}
CodePudding user response:
I grouped your words and then printed them like this.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var list = new List<string>();
// list.Add("Some"); <-- still works
// list.Add("Words");
list.Add("12345");
list.Add("Words");
list.Add("Are");
list.Add("Here");
list.Add("13264");
list.Add("More");
list.Add("Words");
list.Add("15654");
list.Add("Extra");
list.Add("Words");
// list.Add("1234566"); <-- still works
var groups = new Dictionary<int, List<string>>();
int gnum = 0;
foreach (string item in list)
{
if (long.TryParse(item, out long val))
groups.Add( gnum, new List<string>());
else
{
if (!groups.ContainsKey(gnum))
groups.Add(gnum, new List<string>());
groups[gnum].Add(item);
}
}
foreach (var kvp in groups)
{
string result = string.Join(" ", kvp.Value);
Console.WriteLine(result);
}
}
}