bring_it_on
bring_it_on

Reputation: 3

LINQ - How to Flatten List<List<string>> where strings are pipe-delimited

I have a List<List<string>> in C# as below (output from Immediate window):-

macthedList Count = 2  
macthedList[0]  
    [0]: "Rob"  
    [1]: "23"  
    [2]: "Math"  
    [3]: "98"  
macthedList[1]  
    [0]: "Jim"  
    [1]: "25"  
    [2]: "Science|Math|LA"
    [3]: "92|99|89" 

I would like to flatten the above to get a list of 4 lists like below:-

macthedList Count = 4  
macthedList[0]  
    [0]: "Rob"  
    [1]: "23"  
    [2]: "Math"  
    [3]: "98"  
macthedList[1]  
    [0]: "Jim"  
    [1]: "25"  
    [2]: "Science"  
    [3]: "92"  
macthedList[2]  
    [0]: "Jim"  
    [1]: "25" 
    [2]: "Math"  
    [3]: "99"  
macthedList[3]  
    [0]: "Jim"  
    [1]: "25"  
    [2]: "LA"  
    [3]: "89"

Can you please show me how do I do it using LINQ? I guess we use 'SelectMany' but not entirely sure how.

Upvotes: 0

Views: 293

Answers (3)

juharr
juharr

Reputation: 32296

Here's the pure Linq code for that

var result = source.SelectMany(
    x => x[2].Split('|')
        .Zip(x[3].Split('|'), (c, s) => new { Course = c, Score = s })
        .Select(cs => new List<string> { x[0], x[1], cs.Course, cs.Score}))
    .ToList();

Note that if the number of pipes in the 3rd and 4th strings of the sub-lists do not match up this would result in the shorter of the two. It also will throw if any of the sub-lists do not have at least 4 items.

Upvotes: 0

devNull
devNull

Reputation: 4219

This solution solves the specific case you've outlined (disregarding any exceptions to the logic) using SelectMany(), although it's not necessarily pretty:

var matchedList = new List<List<string>> {
    new List<string> { "Rob", "23", "Math", "98" },
    new List<string> { "Jim", "25", "Science|Math|LA", "92|99|89" }
};

List<List<string>> flattened = matchedList.SelectMany(s =>
{
    var courses = s[2].Split('|');
    var grades = s[3].Split('|');
    return courses.Select((c, index) => new List<string> { s[0], s[1], c, grades[index] });
}).ToList();

As others have mentioned, if you want this code to be maintainable, readable, testable, and generally decent you should consider translating these lists into well defined objects that encapsulate this logic.

Upvotes: 1

Alexander Petrov
Alexander Petrov

Reputation: 14251

var source = new List<List<string>> {
    new List<string> { "Rob", "23", "Math", "98" },
    new List<string> { "Jim", "25", "Science|Math|LA", "92|99|89" }
};

var result = new List<List<string>>();

foreach (var list in source)
{
    var name = list[0];
    var age = list[1];

    var courses = list[2].Split('|');
    var scores = list[3].Split('|');

    for (int i = 0; i < courses.Length; i++)
    {
        var temp = new List<string>();

        temp.Add(name);
        temp.Add(age);
        temp.Add(courses[i]);
        temp.Add(scores[i]);

        result.Add(temp);
    }
}
            
foreach (var list in result)
{
    Console.WriteLine(string.Join(", ", list));
}

Upvotes: 0

Related Questions