Problem
I am fairly new to programming and I am having difficulty modulating the following code.
The program reads a file, selects certain requirements, and displays them. I have tried to use passing arrays as arguments or functions as indicated in my textbook. I have been using examples such as int getAges(int array[], integer)
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace Project_2_practice
{
class Program
{
static void Main(string[] args)
{
//Parsing data into memory
string content;
using (StreamReader reader = new StreamReader(File.Open("Data.txt", FileMode.Open)))
{
content = reader.ReadToEnd();
}
string[] rows = content.Split('n'); //Each row is on a new line
string[][] table = new string[rows.Length][];
for (int i = 0; i < rows.Length; table[i] = rows[i].Split(','), i++) ;
//selecting information
int[] districts = new int[rows.Length];
int[] ages = new int[rows.Length];
for (int i = 0; i < rows.Length; i++)
{
districts[i] = int.Parse(table[i][3]);
ages[i] = int.Parse(table[i][0]);
}
//Analyzing selected information
foreach (int district in districts.Distinct().OrderBy(x => x))
Console.WriteLine("District {0} has {1} resident(s)", district, districts.Count(x => x == district));
Console.WriteLine("Ages 0-18 : {0} resident(s)", ages.Count(x => x < 18));
Console.WriteLine("Ages 18-30 : {0} resident(s)", ages.Count(x => x >= 18 && x <= 30));
Console.WriteLine("Ages 31-45 : {0} resident(s)", ages.Count(x => x >= 31 && x <= 45));
Console.WriteLine("Ages 46-64 : {0} resident(s)", ages.Count(x => x >= 46 && x <= 64));
Console.WriteLine("Ages >=65 : {0} resident(s)", ages.Count(x => x >= 65));
}
}
}
Solution
Here’s how I would do it.
Update: I included a description of my code this time based on comments.
First, reading a file into a variable so that each line of the file is its own element in an array is super easy with File.ReadAllLines(…). But I don’t feel like using a variable for that is necessary here, so I immediately start using the array by using the Select(…) method. I’m using Select here to create an anonymous type (That’s the return new { … } part).
var data = File.ReadAllLines(@"c:tempdata.txt").Select (f =>
{
var split = f.Split(',');
return new {
Age = int.Parse(split[0]),
District = split[3]
};
});
At this point, I’ve got a type-safe collection of “data” that I can work with. That’s important, and very useful. For example..
var byDistrict = data.GroupBy (d => d.District);
In the line above, I’ve grouped the data by District. Grouping is, in my opinion, almost always better than using Distinct.
The rest of the code is very similar to your original code, except my code avoids the need to have the age reports within a loop.
foreach (var district in byDistrict)
{
Console.WriteLine("District {0} has {1} resident(s)", district.Key, district.Count());
}
Console.WriteLine("Ages 0-18 : {0} resident(s)", data.Count(x => x.Age < 18));
Console.WriteLine("Ages 18-30 : {0} resident(s)", data.Count(x => x.Age >= 18 && x.Age <= 30));
Console.WriteLine("Ages 31-45 : {0} resident(s)", data.Count(x => x.Age >= 31 && x.Age <= 45));
Console.WriteLine("Ages 46-64 : {0} resident(s)", data.Count(x => x.Age >= 46 && x.Age <= 64));
Console.WriteLine("Ages >=65 : {0} resident(s)", data.Count(x => x.Age >= 65));
I feel as though you are looking at how to really understand the code and how to separate it out into functions and still have all the variables working and all that.
I rewrote it a bit to show how to use different functions with and without return values, with variables passed in, using global variables, and passing in variables by reference.
Take a look.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace Project_2_practice
{
class Program
{
static string[][] table;
static void Main(string[] args)
{
//Parsing data into memory
string[] rows = ReadLines("Data.txt");
table = new string[rows.Length][];
PopulateTable(rows);
//selecting information
int[] districts = new int[rows.Length];
int[] ages = new int[rows.Length];
PopulateArrays(ref districts, ref ages, rows.Length);
//Analyzing selected information
DisplayResults(districts, ages);
}
/// <summary>
/// Read file into memory
/// </summary>
/// <param name="file">string filepath</param>
/// <returns>array of lines in file</returns>
private static string[] ReadLines(string file)
{
return File.ReadAllLines(file);
}
/// <summary>
/// Populates a table with split values from the string
/// </summary>
/// <param name="rows">array of strings to split</param>
private static void PopulateTable(string[] rows)
{
for (int i = 0; i < rows.Length; table[i] = rows[i].Split(','), i++) ;
}
/// <summary>
/// Populates 2 arrays by reference with values from the table variable
/// </summary>
/// <param name="districts">int array</param>
/// <param name="ages">int array</param>
/// <param name="length">int length of table</param>
private static void PopulateArrays(ref int[] districts, ref int[] ages, int length)
{
for (int i = 0; i < length; i++)
{
districts[i] = int.Parse(table[i][3]);
ages[i] = int.Parse(table[i][0]);
}
}
/// <summary>
/// Writes values to console for displaying results
/// </summary>
/// <param name="districts">int array of districts</param>
/// <param name="ages">int array of ages</param>
private static void DisplayResults(int[] districts, int[] ages)
{
foreach (int district in districts.Distinct().OrderBy(x => x))
{
Console.WriteLine("District {0} has {1} resident(s)", district, districts.Count(x => x == district));
}
Console.WriteLine("Ages 0-18 : {0} resident(s)", ages.Count(x => x < 18));
Console.WriteLine("Ages 18-30 : {0} resident(s)", ages.Count(x => x >= 18 && x <= 30));
Console.WriteLine("Ages 31-45 : {0} resident(s)", ages.Count(x => x >= 31 && x <= 45));
Console.WriteLine("Ages 46-64 : {0} resident(s)", ages.Count(x => x >= 46 && x <= 64));
Console.WriteLine("Ages >=65 : {0} resident(s)", ages.Count(x => x >= 65));
}
}
}