Laden...

CSV-Zeilen mit gleichem Datum zusammenfassen

Erstellt von Demokrit vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.099 Views
D
Demokrit Themenstarter:in
10 Beiträge seit 2018
vor 5 Jahren
CSV-Zeilen mit gleichem Datum zusammenfassen

Hallo,

Ich suche seit einem Tag nach einer Lösung und finde tausende Sachen zu dem Thema allgemein, aber nichts was ich persöhnlich auf mein Problem anwenden und verstehen kann.

Ich lese von einer CSV Datei Daten ein. Die Daten werden in einem 2 Dimensionalen Array gespeichert. So weit verstehe ich alles.


16.11.2018 14:17:15;0;5;0;0;0
16.11.2018 14:21:54;2;1;3;1;2
16.11.2018 14:24:38;1;1;0;1;0
16.11.2018 14:25:19;0;5;1;4;2

Die einzelnen Daten werden durch ein ; getrennt.

Problem/Aufgabe:

Ich will das Zeilen, die das gleiche Datum haben, addiert werden also das wenn es zum Beispiel die 2 Zeilen geben würde


16.11.2018 12:18:46;1;1;1;1;1
16.11.2018 12:18:47;1;1;1;1;1

Sollen die Mit dem gleichen datum addiert werden also das es im Prinzip eine Zeile gibt die so aussieht.


16.11.2018 12:18:46;2;2;2;2;2

Meine Idee wäre jetzt jede Zeile einzeln in ein 1-Dimensionales array abzuspeichern und dann dann in ein Dictionary umzuwandeln mit <Date.Time, int[]>.
Ich hab nur leider absolut keine Ahnung wie ich das machen sollte und finde bzw verstehe es auch nicht wirklich wenn ich bei google nachschaue.

Der Ganze Code:


namespace ConsoleApp48
{
    class Program
    {
        static void Main(string[] args)
        {
            string filePath = @"C: \Users\-------------\Csvsave\SaveDatei.csv";
            System.IO.StreamReader sr = new System.IO.StreamReader(filePath);
            var lines = new List<string[]>();
            while (!sr.EndOfStream)
            {
                string[] Line = sr.ReadLine().Split(';');
                lines.Add(Line);
            }

            string[][] DatenSatzAuslesen = lines.ToArray();

            for (int i = 0; i < DatenSatzAuslesen.Length; i++) //i = zeilenindex
            {
                for (int j = 1; j < DatenSatzAuslesen[i].Length; j++) //j = spaltenindex
                {
                    Console.Write("{0} \n",DatenSatzAuslesen[i][j]);
                }
            }
            Console.ReadLine();
        }
    }
}

6.911 Beiträge seit 2009
vor 5 Jahren

Hallo Demokrit,

nimm / erstell dir eine ordentliche Datenstruktur, zerlege es in einfache Methoden und dann geht es fast von alleine.

Schau mal (die Bezeichner hab ich mangels Kontext nicht besser wählen können, sollten aber schon sinnvoller sein):


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            var parser = new Parser();

            Dictionary<DateTime, Value> result = parser.GetMergedItems("data.csv");
        }
    }

    public class Parser
    {
        public Dictionary<DateTime, Value> GetMergedItems(string fileName, bool hasHeader = false)
        {
            var mergedItems = new Dictionary<DateTime, Value>();
            IEnumerable<(DateTime, Value)> items = ParseLines(fileName, hasHeader);

            foreach ((DateTime Date, Value Value) item in items)
            {
                if (mergedItems.ContainsKey(item.Date))
                    mergedItems[item.Date] += item.Value;
                else
                    mergedItems.Add(item.Date, item.Value);
            }

            return mergedItems;
        }

        private static IEnumerable<(DateTime Date, Value Value)> ParseLines(string fileName, bool hasHeader)
        {
            using (StreamReader sr = File.OpenText(fileName))
            {
                if (hasHeader) sr.ReadLine();

                while (!sr.EndOfStream)
                {
                    string line   = sr.ReadLine();
                    string[] cols = line.Split(';');

                    yield return ParseValue(cols);
                }
            }
        }

        private static (DateTime, Value) ParseValue(string[] cols)
        {
            if (!DateTime.TryParse(cols[0], out DateTime dt))
                throw new Exception(".....");

            Action<string, Action<int>> action = (s, setter) =>
            {
                if (!int.TryParse(s, out int tmp))
                    throw new Exception("...");

                setter(tmp);
            };

            Value value = default;
            action(cols[1], i => value.A = i);
            action(cols[2], i => value.B = i);
            action(cols[3], i => value.C = i);
            action(cols[4], i => value.D = i);
            action(cols[5], i => value.E = i);

            return (dt, value);
        }
    }

    [DebuggerDisplay("{A} | {B} | {C} | {D} | {E}")]
    public struct Value
    {
        public int A { get; set; }
        public int B { get; set; }
        public int C { get; set; }
        public int D { get; set; }
        public int E { get; set; }

        public static Value operator +(Value a, Value b)
        {
            return new Value
            {
                A = a.A + b.A,
                B = a.B + b.B,
                C = a.C + b.C,
                D = a.D + b.D,
                E = a.E + b.E
            };
        }
    }
}

Anmerkung: hier geht es explizit um das Addieren von Zeilen / Einträgen mit dem gleichen Datum, daher wurde das Addieren auch direkt einkodiert. Um das generischer zu erledigen, wäre ein "Aggregator" passender und wie hier im konkreten Fall dann ein "SumAggregator" o.ä.
Auf diese Weise kann die Addition einfach gegen z.B. Mittelwert ausgetauscht werden, ohne dass der eigentliche Code verändert werden muss. Das würde dann so ausschauen:


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            var parser     = new Parser();
            var aggregator = new SumAggregator();

            Dictionary<DateTime, Value> result = parser.GetMergedItems("data.csv", aggregator);
        }
    }

    public class Parser
    {
        public Dictionary<DateTime, Value> GetMergedItems(string fileName, IAggregator aggregator, bool hasHeader = false)
        {
            var mergedItems = new Dictionary<DateTime, Value>();
            IEnumerable<(DateTime, Value)> items = ParseLines(fileName, hasHeader);

            foreach ((DateTime Date, Value Value) item in items)
            {
                if (mergedItems.TryGetValue(item.Date, out Value value))
                    mergedItems[item.Date] = aggregator.Operate(item.Value, value);
                else
                    mergedItems.Add(item.Date, item.Value);
            }

            return mergedItems;
        }

        private static IEnumerable<(DateTime Date, Value Value)> ParseLines(string fileName, bool hasHeader)
        {
            using (StreamReader sr = File.OpenText(fileName))
            {
                if (hasHeader) sr.ReadLine();

                while (!sr.EndOfStream)
                {
                    string line = sr.ReadLine();
                    string[] cols = line.Split(';');

                    yield return ParseValue(cols);
                }
            }
        }

        private static (DateTime, Value) ParseValue(string[] cols)
        {
            if (!DateTime.TryParse(cols[0], out DateTime dt))
                throw new Exception(".....");

            Action<string, Action<int>> action = (s, setter) =>
            {
                if (!int.TryParse(s, out int tmp))
                    throw new Exception("...");

                setter(tmp);
            };

            Value value = default;
            action(cols[1], i => value.A = i);
            action(cols[2], i => value.B = i);
            action(cols[3], i => value.C = i);
            action(cols[4], i => value.D = i);
            action(cols[5], i => value.E = i);

            return (dt, value);
        }
    }

    public interface IAggregator
    {
        Value Operate(Value a, Value b);
    }

    public class SumAggregator : IAggregator
    {
        public Value Operate(Value a, Value b) => new Value
        {
            A = a.A + b.A,
            B = a.B + b.B,
            C = a.C + b.C,
            D = a.D + b.D,
            E = a.E + b.E
        };
    }

    [DebuggerDisplay("{A} | {B} | {C} | {D} | {E}")]
    public struct Value
    {
        public int A { get; set; }
        public int B { get; set; }
        public int C { get; set; }
        public int D { get; set; }
        public int E { get; set; }
    }
}

Und dann wäre es noch schön wenn das weitergetriebe wird und Unit-Tests erstellt werden, so dass das korrekte Funktionieren der Klassen / Methoden geprüft werden kann.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

1.564 Beiträge seit 2007
vor 5 Jahren

Hallo

Ist Geschmackssache mit deinem verschachteltem string-Array, aber du kannst z.B. über Linq recht einfach gruppieren und dann Aggretsfunktionen anwenden.

        [TestMethod]
        public void MyTestMethod()
        {
            var arr = new string[][]
            {
                new string[] { "16.11.2018 12:18:46", "1", "1", "1", "1", "1" },
                new string[] { "16.11.2018 12:18:46", "1", "1", "1", "1", "1" },
                new string[] { "17.11.2018 12:18:46", "1", "1", "1", "1", "1" }
            };

            var result = arr.GroupBy(row => row[0])
                            .Select(g => new
                            {
                                Key = g.Key,
                                C1 = g.Sum(row => int.Parse(row[1])),
                                C2 = g.Sum(row => int.Parse(row[2])),
                                C3 = g.Sum(row => int.Parse(row[3])),
                                C4 = g.Sum(row => int.Parse(row[4])),
                                C5 = g.Sum(row => int.Parse(row[5])),
                            }).ToArray();

        }

Viele Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.