diff --git a/docs/csharp/linq/how-to-query-files-and-directories.md b/docs/csharp/linq/how-to-query-files-and-directories.md index 3b27e97cfb320..478f90ecd0c07 100644 --- a/docs/csharp/linq/how-to-query-files-and-directories.md +++ b/docs/csharp/linq/how-to-query-files-and-directories.md @@ -129,5 +129,4 @@ The following text shows how to use the method to :::code language="csharp" source="./snippets/HowToFilesAndDirectories/SumColumns.cs" id="SumColumns"::: -If your file is a tab-separated file, just update the argument in the `Split` method to `\t`. - \ No newline at end of file +If your file is a tab-separated file, just update the argument in the `SumColumns.ProcessColumns` method to `\t`. diff --git a/docs/csharp/linq/snippets/HowToFilesAndDirectories/Program.cs b/docs/csharp/linq/snippets/HowToFilesAndDirectories/Program.cs index 4174dbccb9bc0..f7f3e0d2eaf41 100644 --- a/docs/csharp/linq/snippets/HowToFilesAndDirectories/Program.cs +++ b/docs/csharp/linq/snippets/HowToFilesAndDirectories/Program.cs @@ -32,7 +32,7 @@ Console.WriteLine(); Console.WriteLine("Sum Spreadsheet columns"); -SumColumns.SumCSVColumns("scores.csv"); +SumColumns.ProcessColumns("scores.csv", ","); static void FindFilesByExtension() { diff --git a/docs/csharp/linq/snippets/HowToFilesAndDirectories/SumColumns.cs b/docs/csharp/linq/snippets/HowToFilesAndDirectories/SumColumns.cs index a8db8b22bc761..8b6fb0b8b59d9 100644 --- a/docs/csharp/linq/snippets/HowToFilesAndDirectories/SumColumns.cs +++ b/docs/csharp/linq/snippets/HowToFilesAndDirectories/SumColumns.cs @@ -1,117 +1,59 @@ namespace HowToFilesAndDirectories; // -public class SumColumns +public static class SumColumns { - public static void SumCSVColumns(string fileName) + public static void ProcessColumns(string filePath, string seperator) { - string[] lines = File.ReadAllLines(fileName); - - // Specifies the column to compute. - int exam = 3; - - // Spreadsheet format: - // Student ID Exam#1 Exam#2 Exam#3 Exam#4 - // 111, 97, 92, 81, 60 - - // Add one to exam to skip over the first column, - // which holds the student ID. - SingleColumn(lines, exam + 1); - Console.WriteLine(); - MultiColumns(lines); - } - - static void SingleColumn(IEnumerable strs, int examNum) - { - Console.WriteLine("Single Column Query:"); - - // Parameter examNum specifies the column to - // run the calculations on. This value could be - // passed in dynamically at run time. - - // Variable columnQuery is an IEnumerable. - // The following query performs two steps: - // 1) use Split to break each row (a string) into an array - // of strings, - // 2) convert the element at position examNum to an int - // and select it. - var columnQuery = from line in strs - let elements = line.Split(',') - select Convert.ToInt32(elements[examNum]); - - // Execute the query and cache the results to improve - // performance. This is helpful only with very large files. - var results = columnQuery.ToList(); - - // Perform aggregate calculations Average, Max, and - // Min on the column specified by examNum. - double average = results.Average(); - int max = results.Max(); - int min = results.Min(); - - Console.WriteLine($"Exam #{examNum}: Average:{average:##.##} High Score:{max} Low Score:{min}"); + // Divide each exam into a group + var exams = from line in MatrixFrom(filePath, seperator) + from score in line + + // Identify the column number + let colNumber = Array.FindIndex(line, t => ReferenceEquals(score, t)) + + // The first column is the student ID, not the exam score + // so it needs to be excluded + where colNumber > 0 + + // Convert the score from string to int + // Group by column number, i.e. one group per exam + group double.Parse(score) by colNumber into g + select new + { + Title = $"Exam#{g.Key}", + Min = g.Min(), + Max = g.Max(), + Avg = Math.Round(g.Average(), 2), + Total = g.Sum() + }; + + foreach (var exam in exams) + { + Console.WriteLine($"{exam.Title}\t" + + $"Average:{exam.Avg,6}\t" + + $"High Score:{exam.Max,3}\t" + + $"Low Score:{exam.Min,3}\t" + + $"Total:{exam.Total,5}"); + } } - static void MultiColumns(IEnumerable strs) + // Transform the file content to an IEnumerable of string arrays + // like a matrix + private static IEnumerable MatrixFrom(string filePath, string seperator) { - Console.WriteLine("Multi Column Query:"); - - // Create a query, multiColQuery. Explicit typing is used - // to make clear that, when executed, multiColQuery produces - // nested sequences. However, you get the same results by - // using 'var'. + using StreamReader reader = File.OpenText(filePath); - // The multiColQuery query performs the following steps: - // 1) use Split to break each row (a string) into an array - // of strings, - // 2) use Skip to skip the "Student ID" column, and store the - // rest of the row in scores. - // 3) convert each score in the current row from a string to - // an int, and select that entire sequence as one row - // in the results. - var multiColQuery = from line in strs - let elements = line.Split(',') - let scores = elements.Skip(1) - select (from str in scores - select Convert.ToInt32(str)); - - // Execute the query and cache the results to improve - // performance. - // ToArray could be used instead of ToList. - var results = multiColQuery.ToList(); - - // Find out how many columns you have in results. - int columnCount = results[0].Count(); - - // Perform aggregate calculations Average, Max, and - // Min on each column. - // Perform one iteration of the loop for each column - // of scores. - // You can use a for loop instead of a foreach loop - // because you already executed the multiColQuery - // query by calling ToList. - for (int column = 0; column < columnCount; column++) + for (string? line = reader.ReadLine(); line is not null; line = reader.ReadLine()) { - var results2 = from row in results - select row.ElementAt(column); - double average = results2.Average(); - int max = results2.Max(); - int min = results2.Min(); - - // Add one to column because the first exam is Exam #1, - // not Exam #0. - Console.WriteLine($"Exam #{column + 1} Average: {average:##.##} High Score: {max} Low Score: {min}"); + yield return line.Split(seperator, StringSplitOptions.TrimEntries); } } } -/* Output: - Single Column Query: - Exam #4: Average:76.92 High Score:94 Low Score:39 - Multi Column Query: - Exam #1 Average: 86.08 High Score: 99 Low Score: 35 - Exam #2 Average: 86.42 High Score: 94 Low Score: 72 - Exam #3 Average: 84.75 High Score: 91 Low Score: 65 - Exam #4 Average: 76.92 High Score: 94 Low Score: 39 - */ +// Output: +// Exam#1 Average: 86.08 High Score: 99 Low Score: 35 Total: 1033 +// Exam#2 Average: 86.42 High Score: 94 Low Score: 72 Total: 1037 +// Exam#3 Average: 84.75 High Score: 91 Low Score: 65 Total: 1017 +// Exam#4 Average: 76.92 High Score: 94 Low Score: 39 Total: 923 //