0

I have a csv file with repetitive keys and multiple values from a csv. I have successfully parsed every row of csv in C#

My csv file looks like this

eid,hr
ABC,25
ABC,35
FDG,50
LMN,61

Task1 I would want to construct a dictionary

like Dictionary<string,int[]> or Dictonary<string,List>

key1: ABC value1: 25
key2: ABC value2: 35
key3: FDG value3: 50
key4: LMN value4: 61

Task 2

Iterate through keys and make sure the total of the values for the same key is more than 50. value1 and value2 will be proportioned to make the total as 50.

Here is the solution:

    namespace CSVRead {class Program
    {
        static void Main(string[] args)
        {
            
            string[] csvLines = System.IO.File.ReadAllLines("//TheCSVfilepath");

            var eID = new List<string>();
            var hr = new List<double>();


            for ( int i = 1; i < csvLines.Length; i++)
            {
                string[] rowData = csvLines[i].Split(',');

                eID.Add(rowData[0]);

                double hre = Convert.ToDouble(rowData[7]);
                hr.Add(hre);
             

            }

            Console.WriteLine("eidDict:  ");
            for ( int m =0 ; m < eID.Count ; m++)
            {
                List<(string Key, double Value)> list = new List<(string Key, double Value)>; //CS1526Error
                var eIDdictlist = list;

                for (int n =0; n < hr.Count; n++)
                {
                    employedictlist.Add(new KeyValuePair<string, double>(eID[m], hr[n])); //CS1503 Error
                    Console.WriteLine(eIDdictlist);
                }
            }

            Console.ReadKey();

        }
    }
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
exd-as
  • 23
  • 1
  • 7

2 Answers2

1

Your code doesn't seem to do remotely what your question is asking about. Here's what you need to do to get a dictionary of the list of values:

     static void Main(string[] args)
    {

        var csvLines = File.ReadAllLines("//TheCSVfilepath");

        var dictionary = new Dictionary<string, List<double>>();
        foreach (var line in csvLines)
        {
            var (eID, hr) = GetEidAndHr(line);
            if (!dictionary.TryGetValue(eID, out var hrList))
            {
                hrList = new List<double>();
                dictionary[eID] = hrList;
            }

            hrList.Add(hr);
        }

        Console.WriteLine("eidDict:  ");
        //foreach (var keyValuePair in dictionary) // for pre c# 8
        foreach (var (eid,hrList) in dictionary)
        {
           // var eid = keyValuePair.Key;// for pre c# 8
           // var hrList = keyValuePair.Value;// for pre c# 8
            var sum = hrList.Sum();
            Console.WriteLine($"{eid}, {sum}");
        }
    }

    static Tuple<string, double> GetEidAndHr(string csvLine)
    {
        var rowData = csvLine.Split(',');
        return new Tuple<string, double>(rowData[0],double.Parse(rowData[7]));
    }

If you are just trying to get your code to compile, here is what you need to change:

List<(string Key, double Value)> list = new List<(string Key, double Value)>; //CS1526Error

Needs to become

List<(string Key, double Value)> list = new List<(string Key, double Value)>();

And you also need to declare your variable employedictlist before you use it.

Nigel
  • 2,961
  • 1
  • 14
  • 32
  • Thanks Nigel for sharing the answer. Unfortunately I get 4 errors: CS8129- No suitable 'Deconstruct' instance or extension method was found for type 'KeyValuePair>', with 2 out parameters and a void return type,CS1061-- 'KeyValuePair>' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'KeyValuePair>' could be found (are you missing a using directive or an assembly reference?)= – exd-as Oct 22 '21 at 21:18
  • 1
    @exdas that just means your C# language version is set to something before C# 8.0. I updated my answer with some commented lines that you can use for C#7 or earlier – Nigel Oct 22 '21 at 21:23
  • Also, the foreach loop has typo eid must eID – exd-as Oct 22 '21 at 21:23
  • return new Tuple(rowData[0], double.Parse(rowData[6])); >>>> System.FormatException: 'Input string was not in a correct format.' ??. Please help . Thanks Nigel . I appreciate this – exd-as Oct 22 '21 at 21:37
  • 1
    whatever row you were on did not have 8 or more items. I just used the indexing you provided which optimistically assumes at least 8 items per row. You will need to decide on your own what happens when a row has less than 8 items. – Nigel Oct 22 '21 at 21:38
  • I have hr as my 6th item in row. I changed to rowData[6], i got the same error. Any suggestions, Please? – exd-as Oct 22 '21 at 23:01
  • @exdas 6th item is rowData[5] – Nigel Oct 22 '21 at 23:04
  • @exdas please upvote and mark as best answer if you found it helpful! – Nigel Oct 22 '21 at 23:05
  • Voted !! Thank you again. I checked my csv and did rowData[5] still the same issue – exd-as Oct 22 '21 at 23:18
0

I think the following code should work for you:

// Test data to mock data from the CSV file
static IEnumerable<(string key, double value)> ReadCsv() =>
    new[]
    {
        ("ABC", 42.0),
        ("ABC", 123.0),
        ("DEF", 35.0),
        ("DEF", 15.0)
    };

static void Main(string[] args)
{
    // Task 1: Constructing a Dictionary<string, List<double>>
    var data = ReadCsv()
        .GroupBy(entry => entry.key)
        .ToDictionary(
            group => group.Key,
            group => group.Select(entry => entry.value));

    // Task 2: Iterating through the keys and verifying the total
    var isSuccess = data.All(entry => entry.Value.Sum() > 50);
    if (isSuccess)
        Console.WriteLine("The total of values for each and every entry is greater than 50");
    else
        Console.WriteLine("The total of values for each and every entry is not greater than 50");
}

To read data from a CSV file, I would suggest using something like:

static IEnumerable<(string key, double value)> ReadCsv(string filePath, uint nHeaderLines = 0)
{
    using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
    using (var reader = new StreamReader(stream))
    {
        var lineIndex = 0u;
        while (reader.ReadLine() is string dataEntry)
        {
            if (lineIndex++ < nHeaderLines                // Skip header
                || string.IsNullOrWhiteSpace(dataEntry))  // Ignore blank lines
            {
                continue;
            }

            var dataElements = dataEntry.Split(',', StringSplitOptions.RemoveEmptyEntries);
            var key = dataElements[0];
            var value = double.Parse(dataElements[7], NumberStyles.Float, NumberFormatInfo.InvariantInfo);

            yield return (key, value);
        }
    }
}

Please note, that for the real solution it would also make sense to check the data line if it has all the entries, i.e., that elements at index 0 and 7 always exist and are valid

Volodymyr
  • 257
  • 2
  • 5
  • Could you please let me know what is the issue ?? Exception Unhandled: System.FormatException : " Input string was not in a correct format ." – exd-as Oct 22 '21 at 23:53
  • 1
    @exdas First thing you can try is to change the parsing line to `var value = double.Parse(dataElements[7], NumberStyles.Float, NumberFormatInfo.InvariantInfo);`. If this does not work, you can try `var value = Convert.ToDouble(dataElements[7]);`. If this does not work too, please provide the sample value in your `dataElements[7]` entry in a comment. I updated the sample code. – Volodymyr Oct 23 '21 at 06:15
  • eid,pre,dpt,ccd,pcd,mle,hr XXX1023,AB-564-611,AEMP,18&135,MM,13-5-2018,32 XXX8023,AB-545-611,AMAN,18&135,4,15-5-2011,8 – exd-as Oct 24 '21 at 17:47
  • From eid to hr is my header for the csv. The above comment is a sample of csv. Please let me know, i still have the same issue. – exd-as Oct 24 '21 at 17:56
  • 1
    @exdas The issue is that `dataElements[7]` with the data sample you provided would have a value of `AB-564-611`, which is definitely not a `double`. What value (or what index) from the data entry do you want to have in the output? Or does the CSV file contain a header line that has to be skipped and the data follow in the next rows? – Volodymyr Oct 24 '21 at 21:25
  • 1
    @exdas I modified the `ReadCsv(...)` method to take into account header and blank lines. Please make sure you are parsing the data at the correct indices. If you want to parse data from `eid` and `hr XXX1023` columns, you shall take `dataElements[0]` and `dataElements[6]`, **not** `dataElements[7]`. Hope this helps. – Volodymyr Oct 24 '21 at 21:56
  • @exdas If the solution solved your question and helped you, please mark it as an accepted answer and upvote. – Volodymyr Oct 26 '21 at 06:22
  • I am getting the same issue. But i think all solutions mentioned below has the same issue. So i will mention this as helpful. I think their is an issue with my csv file. Is their a way i can email you my file? – exd-as Oct 26 '21 at 13:50
  • could you please let me know what is dataEntry in your code? – exd-as Oct 26 '21 at 13:59
  • 1
    @exdas `dataEntry` is a string that represents a single line of data that is being read from a CSV file. It's set in the `while (reader.ReadLine() is string dataEntry)` loop. The loop will read the file line by line until it reaches its end. – Volodymyr Oct 26 '21 at 15:14
  • Thanks Volodymyr. I appreciate this – exd-as Oct 26 '21 at 15:19