Linq group data in flat enumerated

Period|1|
AA|0|0|32.39|0|0|-0.12|
BB|0|-1794.62|
CC|Entity1|25|31.48|244.1|
DD|Entity2|25|0|0|
Period|2|
AA|0|0|32.39|0|0|-0.12|
BB|0|-1794.62|
CC|Entity1|25|31.48|244.1|
EE|Entity2|25|0|0|
FF|Entity3|25|0|0|
GG|Entity4|25|0|0|
HH|Entity5|25|0|0|
Period|3|
AA|0|0|32.39|0|0|-0.12|
BB|0|-1794.62|

Consider the above collection as:

IEnumerable<IEnumerable<string>> data;

The first line of Enumerable is each line. Second Enumerable - each line separated by a delimiter |

I would like to group this by each period:

Expected Result:

Period1 (Group Key)
         AA|0|0|32.39|0|0|-0.12|
         BB|0|-1794.62|
         CC|Entity1|25|31.48|244.1|
         DD|Entity2|25|0|0|
Period2 (Group Key)
         AA|0|0|32.39|0|0|-0.12|
         BB|0|-1794.62|
         CC|Entity1|25|31.48|244.1|
         EE|Entity2|25|0|0|
         FF|Entity3|25|0|0|
         GG|Entity4|25|0|0|
         HH|Entity5|25|0|0|
Period3 (Group Key)
        AA|0|0|32.39|0|0|-0.12|
        BB|0|-1794.62|

Current implementation:

foreach (var dataPerPeriod in data.Take(5))
{
    yield return new DataPerPeriod(dataPerPeriod);
}

but, as you can see, only the first period has 5 elements, including a potential key element (period).

Therefore, I do not understand how to approach this problem.

+4
source share
4 answers

I created a help class for your DataPerPeriod:

public class DataPerPeriod
{
    public string Name { get; set; }
    public List<IEnumerable<string>> Lines { get; set;}
}

How could I combine it with this query:

var res = data.Aggregate(new List<DataPerPeriod>(), (a, b) =>
{
    if (b.First() =="Period")
    {
        a.Add(new DataPerPeriod { Name = String.Join("", b),
                                  Lines = new List<IEnumerable<string>>() });
    }
    else
    {
        a.Last().Lines.Add(b);
    }
    return a;
});

Result:

enter image description here

+4
source

LINQ, LINQ spirit, () :

public static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, Func<T, bool> splitOn)
    {
        using (var e = source.GetEnumerator())
        {
            for (bool more = e.MoveNext(); more;)
            {
                var group = new List<T> { e.Current };
                while ((more = e.MoveNext()) && !splitOn(e.Current))
                    group.Add(e.Current);
                yield return group;
            }
        }
    }
}

:

IEnumerable<IEnumerable<string>> source = ...;
var result = source
    .Split(e => e.FirstOrDefault() == "Period")
    .Select(g => new
    {
        Key = g.First().Skip(1).FirstOrDefault(),
        Elements = g.Skip(1)
    });
+2

Not particularly elegant (but not your data set either), but this works:

public static Dictionary<IEnumerable<string>,IEnumerable<IEnumerable<string>>> Parse(IEnumerable<IEnumerable<string>> input)
{
    IEnumerable<string> key = null;
    var rows = new List<IEnumerable<string>>();
    var result = new Dictionary<IEnumerable<string>,IEnumerable<IEnumerable<string>>>();

    foreach(var row in input)
    {
        if(row.First().StartsWith("Period"))
        {
            if(key != null)
                result.Add(key,rows.AsEnumerable());

            key = row;
            rows = new List<IEnumerable<string>>();
        }
        else
        {
            rows.Add(row);
        }
    }
    result.Add(key,rows);
    return result;
}

Real-time example: http://rextester.com/ZMUM90524

+1
source

I have been parsing text files for 40 years. If I can’t do this, no one can. My solution is almost the same as that of Jamiec, a little different in style

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;



namespace ConsoleApplication43
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.txt";
        static void Main(string[] args)
        {
            StreamReader reader = new StreamReader(FILENAME);
            string inputLine = "";

            Dictionary<string, List<string>> data = new Dictionary<string, List<string>>();

            List<string> period = null;
            while ((inputLine = reader.ReadLine()) != null)
            {
                inputLine = inputLine.Trim();
                if (inputLine.Length > 0)
                {
                    if (inputLine.StartsWith("Period"))
                    {
                        string key = inputLine.Replace("|", "");
                        period = new List<string>();
                        data.Add(key, period);
                    }
                    else
                    {
                        period.Add(inputLine);
                    }
                }
            }
        }
    }
}
0
source

Source: https://habr.com/ru/post/1667771/


All Articles