Home > database >  Remove all the occurences that contain a part of the initial string
Remove all the occurences that contain a part of the initial string

Time:12-17

I have a list of string that contains for example the following items:

0$1, 0$2, 0$5, 1$1, 1$8

My will is remove the other items with $1 if one already exists. In that example, the expected output would be:

0$1, 0$2, 0$5, 1$8

Is there a cleaner way to achieve this, with LINQ or anything else, rather than doing something like this?

For Each str As String In ddlElecDBFilter.SelectedItemsList
    Dim currentString As String = str
    'DO A FOR EACH LOOP on the other elements (starting from the current index   1 and delete them 

Next

Thank you in advance

CodePudding user response:

Not sure if there is a "cleaner" way to do this:

Dim input As String = "0$1, 0$2, 0$5, 1$1, 1$8"

Dim uniqueValues As New List(Of String)
input.Split(", ".ToCharArray, StringSplitOptions.RemoveEmptyEntries) _
    .Where(Function(x) x.Contains("$")) _
    .Select(Function(y) New KeyValuePair(Of String, String)(y.Substring(y.IndexOf("$")), y)) _
    .ToList() _
    .ForEach(Sub(pair)
                 If Not uniqueValues.Any(Function(x) x.EndsWith(pair.Key)) Then
                     uniqueValues.Add(pair.Value)
                 End If
             End Sub)
Dim output As String = String.Join(", ", uniqueValues)

CodePudding user response:

Since you said C# was acceptable, here my answer is for C#. If you have trouble translating to VB.Net, I can add that.

I would not say that LINQ is cleaner, but it is short. When you need to accumulate items, Aggregate is the method to use. When you need to keep some type of state while processing, Aggregate with a ValueTuple (or some other container) allows you to preserve a value while processing.

If ss contains your List<string>, then ans will contain the processed strings:

var ans = ss.Select(s => s.Split(", ")
            .Aggregate((ans: Enumerable.Empty<string>(), seen: false), // accumulate result in ans, track whether $1 has been seen
                       (ans_seen, item) => ans_seen.seen
                                            ? (item.EndsWith("$1") ? ans_seen : (ans_seen.ans.Append(item), ans_seen.seen))
                                            : (ans_seen.ans.Append(item), item.EndsWith("$1"))
                      )
            .ans.Join(", ")) // string extension method that encapsulates String.Join
            .ToList();

This uses an obvious string extension method to encapsulate String.Join.

I would say it is better to create a function that uses looping and just use LINQ for the List processing:

public static class StringExt {
    public static string KeepOnlyFirstDollarOne(this string s) {
        var seen = false;
        var resultList = new List<string>();
        foreach (var item in s.Split(", ")) {
            if (seen) {
                if (!item.EndsWith("$1"))
                    resultList.Add(item);
            }
            else {
                resultList.Add(item);
                seen = item.EndsWith("$1");
            }
        }
        return String.Join(", ", resultList);
    }
}

Then getting an answer is as simple as:

var ans = ss.Select(s => s.KeepOnlyFirstDollarOne()).ToList();
  • Related