Home > Software design >  Removing strings from a list line by line
Removing strings from a list line by line

Time:08-16

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

  1. Start with an empty temporary list for the current "page" of items.
  2. Loop through each element in your source.
  3. 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.
  4. 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);
          
       }
        
    }
}
  • Related