Search code examples
linqlinq-group

Grouping resulting in anonymous types


I have a list of strings such as this one

heading1 00:01:20
randomText
01:23
randomText2
01:45
randomText3
02:10
heading2 00:05:20
randomText4
07:25
randomText5
04:35
randomText6
09:12
etc.

What I'd like to do is using Linq to get a list of anonymous types such as

{ Name = "randomText1", Category = "Heading1", Duration = "01:23"}
{ Name = "randomText2", Category = "Heading1", Duration = "01:45"}
...
{ Name = "randomText6", Category = "Heading2", Duration = "09:12"}

Is there any way I could achieve such a result with Linq to Objects? I see how it would be easy to achieve this using for loops, but I am wondering if there would be a cleaner way to do this using Linq, and I can't seem to find any.


Solution

  • This does the trick with the following conditions:

    1) A heading line can be recognized, it starts with a constant value (you can change this condition if you want)

    2) For every 'randomtext'-line, a corresponding duration-line can be found.

    var data = new List<string>() {
        "heading1 00:01:20",
        "randomText       ",
        "01:23            ",
        "randomText2      ",
        "01:45            ",
        "randomText3      ",
        "02:10            ",
        "heading2 00:05:20",
        "randomText4      ",
        "07:25            ",
        "randomText5      ",
        "04:35            ",
        "randomText6      ",
        "09:12            "
    };
    
    const string HEADINGSTART = "heading";                      // TODO: Set correct value
    var temp = Enumerable.Range(0, data.Count - 1)              // Iterate based on index
        .Where(i => data[i].StartsWith(HEADINGSTART))           // Find the headings
        .Select(i => new {                                      // Project to heading + data
            Heading = data[i],
            Data = data.Skip(i + 1).TakeWhile(d => !d.StartsWith(HEADINGSTART)).ToList()
        })
        .SelectMany(d => d.Data.Select(d2 => new {              // Project to single enumerable
            Heading = d.Heading,
            Data = d2
        }))
        .ToList();
    var result = Enumerable.Range(0, temp.Count / 2)            // Again, iterate based on index
        .Select(i => new {                                      // Project to requested object
            Name = temp[i * 2].Data,
            Category = temp[i * 2].Heading,
            Duration = temp[i * 2 + 1].Data
        })
        .ToList();