0

I'm currently making a program that tracks certain things (basic INT Values and the Date when they were saved).

My goal is to add up the INT values with the same Date.

20.11.2018 00:00:00; 1;1;1;1;1 20.11.2018 00:00:00; 1;1;1;1;1 22.11.2018 00:00:00; 1;1;1;1;1 

Should basically look like this

20.11.2018 00:00:00; 2;2;2;2;2 22.11.2018 00:00:00; 1;1;1;1;1 

The Saving Data and even the adding the 2 "Lines" together is working perfectly fine.

The problem is that When I add the Lines together, the 2 Lines with the 1 obviously don't get deleted.

This is the Method that Compares the Date and adds the lines together:

public static Dictionary<DateTime, int[]> CompareDateMethod(Dictionary<DateTime, int[]> oDateTimeAndIntDictionary,string[][] ReadData) { Dictionary<DateTime, int[]> oPrintRealData = new Dictionary<DateTime, int[]>(); Dictionary<DateTime, int[]> oAddRealData = new Dictionary<DateTime, int[]>(); for (int i = 0 ; i < ReadData.Length; i++) { DateTime dtDateValue; if (DateTime.TryParse(ReadData[i][0], out dtDateValue)) { int[] iValuesToAdd = ConvertArrayToInt(ReadData[i]); if (dtDateValue.Date == DateTime.Now.Date) { for (int j = 0; j < iValuesToAdd.Length; j++) { oDateTimeAndIntDictionary[dtDateValue.Date][j] += iValuesToAdd[j]; } } else if (dtDateValue.Date != DateTime.Now.Date) { goto Endloop; } } } Endloop: return oDateTimeAndIntDictionary; 

This is the method that Writes the Data into the .CSV file

 Dictionary<DateTime, int[]> oDateTimeAndIntDictionary = new Dictionary<DateTime, int[]>(); string[][] OldData= AddVariables.ReadOldData(); int[] iNewDataArray = new int[] { iVariable1, iVariable2, iVariable3, iVariable4, iVariable5}; oDateTimeAndIntDictionary.Add(DateTime.Now.Date, iNewDataArray); using (System.IO.FileStream fileStream = new System.IO.FileStream(@"C: \Users\---\Csvsave\SaveDatei.csv", System.IO.FileMode.Append, System.IO.FileAccess.Write)) using (System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(fileStream)) { foreach (KeyValuePair<DateTime, int[]> kvp in AddVariables.CompareDateMethod(oDateTimeAndIntDictionary, OldData)) { streamWriter.WriteLine("{0}; {1}", kvp.Key, string.Join(";", kvp.Value)); } } 

I tried so hard to come up with something but nothing worked (I tried deleting lines from the .csv which seems really hard, I tried reading the file in backwards which didnt work etc.)

If someone can give me some pointers I would really appreciate it.

9
  • So the problem is that when you run it the second time the result gets added to the CSV instead of replacing the existing data? Commented Nov 22, 2018 at 14:29
  • I think the Data only gets added once. The problem is that the when i add line 1-2 (with the same date) I get line 3. I only need line 3 because line 1 and 2 are useless now. Commented Nov 22, 2018 at 14:31
  • joshclose.github.io/CsvHelper when playing with a csv Commented Nov 22, 2018 at 14:35
  • @DragandDrop im trying not to use libraries and stuff like linq yet because I dont really understand them. I just started learning c# and I want to get the basics down before I start using advanced stuff. Commented Nov 22, 2018 at 14:37
  • I think the problem here is that you're writing a value to the file for every line you read. You need to restructure your loops to be able to read multiple times and only write when you find a new date. Commented Nov 22, 2018 at 14:40

4 Answers 4

1

I think the problem with the original code is that it's a bit confused about what happens when. I've restructured it so that things happen in a logical order (and updated it a bit, simplifying variable names, etc). There's one function for combining rows with the same date, which is separate from the CSV writing code (which hasn't changed)

static void Main(string[] args) { var oldData = ReadOldData(); // Do the work var results = SumValuesForSameDate(oldData); // Write the file using (System.IO.FileStream fileStream = new System.IO.FileStream(@"C: \Users\---\Csvsave\SaveDatei.csv", System.IO.FileMode.Append, System.IO.FileAccess.Write)) using (System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(fileStream)) { foreach (KeyValuePair<DateTime, int[]> kvp in results) { streamWriter.WriteLine("{0}; {1}", kvp.Key, string.Join(";", kvp.Value)); } } } public static Dictionary<DateTime, int[]> SumValuesForSameDate(string[][] readData) { var oDateTimeAndIntDictionary = new Dictionary<DateTime, int[]>(); var currentDate = DateTime.MinValue; foreach (var row in readData) { DateTime dateValue; if(!DateTime.TryParse(row[0], out dateValue)) continue; dateValue = dateValue.Date; var intValues = ConvertArrayToInt(row); if (dateValue == currentDate) { for (var j = 0; j < intValues.Length; j++) { oDateTimeAndIntDictionary[dateValue][j] += intValues[j]; } } else { oDateTimeAndIntDictionary.Add(dateValue, intValues); currentDate = dateValue; } } return oDateTimeAndIntDictionary; } static int[] ConvertArrayToInt(string[] strings) { var output = new int[strings.Length - 1]; for (var i = 1; i < strings.Length; i++) { output[i - 1] = int.Parse(strings[i]); } return output; } static string[][] ReadOldData() { // Fake data var data = new string[][] { new string[] { "20.11.2018 00:00:00", "1", "1", "1", "1", "1" }, new string[] { "20.11.2018 00:00:00", "1", "1", "1", "1", "1" }, new string[] { "22.11.2018 00:00:00", "1", "1", "1", "1", "1" }, }; return data; } } 
Sign up to request clarification or add additional context in comments.

6 Comments

Do you think its possible without using a 2 dimensional array at all ? Im trying it right now
@Demokrit you can try List<string[]> with which you can play around using LINQ...
@Vanest perfect thats what I did before the only problem right now is that I am having trouble reading the Date out of the File with the old data. Do you know how I would need to modify this if (DateTime.TryParse(oReadCsvList[i][0], out OldDateTime)) to make it work with a list ?
@Demokrit - yes, personally would have created a lightweight Row class with a Date and List<int> properties. It could have a ToString() method for writing to the CSV file, and even a method to add the values from another Row. However I wanted a solution that was recognisable from the original, in case I'd missed some important feature.
@Demokrit foreach (string[] csvDate in oReadCsvList) { if (DateTime.TryParse(csvDate[0], out OldDateTime)) { } } that also works the same way.
|
0

For overwriting the previous CSV just use System.IO.FileMode.Create instead of Append. This will overwrite any previous data.

6 Comments

But that would just delete the lines that don't have the same Date right ? That means I would need to read in all the values first ?
Looking at your code, it looks like you are recalculating only the data for today (dtDateValue.Date == DateTime.Now.Date) so in that case, before writing the data, I would read the last line of the CSV and if the last line has today's data I would remove it. Also you would need to make sure that the items are added chronologically in your CSV (ordered by date)
The answer on this thread looks more interesting: forums.asp.net/t/1622656.aspx?Delete+last+line+in+a+text+file. You read the CSV again, check if the line you are trying to enter is not there already, remove it if it is, and then add the new calculation in it's place. This should be generic enough to handle any update on any date not only today.
the delete last line looks like a possible solution to me. Ill have to try that tomorrow and get back to you thank you so much for the pointer though.
Solutions that require adding and deleting lines while reading from a file are likely to go wrong. IMHO it's better to read the input file, build the output, then overwrite the input file if you're happy with it.
|
0

You need to overwrite the csv anyways to get rid of the written row. So instead of returning oDateTimeAndIntDictionary from CompareDateMethod method, return ReadData and overwrite the parsed values of ReadData.

Something like this,

public static Dictionary<DateTime, int[]> CompareDateMethod(Dictionary<DateTime, int[]> oDateTimeAndIntDictionary,string[][] ReadData) { Dictionary<DateTime, int[]> oPrintRealData = new Dictionary<DateTime, int[]>(); Dictionary<DateTime, int[]> oAddRealData = new Dictionary<DateTime, int[]>(); for (int i = 0 ; i < ReadData.Length; i++) { DateTime dtDateValue; if (DateTime.TryParse(oDateTimeAndIntDictionary[0][0], out dtDateValue)) { int[] iValuesToAdd = ConvertArrayToInt(ReadData[i]); if (dtDateValue.Date == DateTime.Now.Date) { for (int j = 0; j < iValuesToAdd.Length; j++) { //Add the ReadData values here and store at ReadData[i][j] } } else if (dtDateValue.Date != DateTime.Now.Date) { goto Endloop; } } } Endloop: return ReadData; } 

Hope this helps...

Comments

0

I readed your comment about not using linq and 3rd part lib too late.
But let me show you what you are missing.
Here it's a little Linq + CSVHelper

First Lest define your data, and define how to map them in the CSV

public sealed class data { public DateTime TimeStamp { get; set; } public List<int> Numbers { get; set; } } public sealed class dataMapping : ClassMap<data> { public dataMapping() { Map(m => m.TimeStamp).Index(0); Map(m => m.Numbers) .ConvertUsing( row => new List<int> { row.GetField<int>(1), row.GetField<int>(2), row.GetField<int>(3) } ); } } 

And now this is a short demo:

class CsvExemple { string inputPath = "datas.csv"; string outputPath = "datasOut.csv"; List<data> datas; public void Demo() { //no duplicate row in orginal input InitialiseFile(); LoadExistingData(); //add some new row and some dupe NewDatasArrived(); //save to an other Path, to Compare. SaveDatas(); } private void SaveDatas() { using (TextWriter writer = new StreamWriter(outputPath)) using (var csvWriter = new CsvWriter(writer)) { csvWriter.Configuration.RegisterClassMap<dataMapping>(); csvWriter.Configuration.Delimiter = ";"; csvWriter.Configuration.HasHeaderRecord = false; csvWriter.WriteRecords(datas); } } static List<int> SuperZip(params List<int>[] sourceLists) { for (var i = 1; i < sourceLists.Length; i++) { sourceLists[0] = sourceLists[0].Zip(sourceLists[i], (a, b) => a + b).ToList(); } return sourceLists[0]; } private void NewDatasArrived() { var now = DateTime.Today; // New rows var outOfInitialDataRange = Enumerable.Range(11, 15) .Select(x => new data { TimeStamp = now.AddDays(-x), Numbers = new List<int> { x, x, x } }); // Duplicate rows var inOfInitialDataRange = Enumerable.Range(3, 7) .Select(x => new data { TimeStamp = now.AddDays(-x), Numbers = new List<int> { x, x, x } }); //add all of them them together datas.AddRange(outOfInitialDataRange); datas.AddRange(inOfInitialDataRange); // all this could have been one Line var grouped = datas.GroupBy(x => x.TimeStamp); var temp = grouped.Select(g => new { TimeStamp = g.Key, ManyNumbers = g.Select(x => x.Numbers).ToArray() }); // We can combine element of 2 list using Zip. ListA.Zip(ListB, (a, b) => a + b) datas = temp.Select(x => new data { TimeStamp = x.TimeStamp, Numbers = SuperZip(x.ManyNumbers) }).ToList(); } private void LoadExistingData() { if (File.Exists(inputPath)) { using (TextReader reader = new StreamReader(inputPath)) using (var csvReader = new CsvReader(reader)) { csvReader.Configuration.RegisterClassMap<dataMapping>(); csvReader.Configuration.HasHeaderRecord = false; csvReader.Configuration.Delimiter = ";"; datas = csvReader.GetRecords<data>().ToList(); } } else { datas = new List<data>(); } } private void InitialiseFile() { if (File.Exists(inputPath)) { return; } var now = DateTime.Today; var ExistingData = Enumerable.Range(0, 10) .Select(x => new data { TimeStamp = now.AddDays(-x), Numbers = new List<int> { x, x, x } }); using (TextWriter writer = new StreamWriter(inputPath)) using (var csvWriter = new CsvWriter(writer)) { csvWriter.Configuration.RegisterClassMap<dataMapping>(); csvWriter.Configuration.Delimiter = ";"; csvWriter.Configuration.HasHeaderRecord = false; csvWriter.WriteRecords(ExistingData); } } } 

2 Comments

By geting the Csv configuration in a method, removing the initialisation to pretend that there is data and cutting the fluff , reading + solving duplicate and saving it's 13 lines of codes.
Super Zip could be more efficient than summing all value to the first element but I wanted to have a clear code on this part.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.