How to create dictionary for students and multiple courses grades? - c#

I am learning C# dictionary and I am confused how to do this
I have a Dictionary of students names. and I would like to assign another dictionary to that.
my input similar to this
Student1:
Math,9
Science,5
English,2
Student2:
Math,9
Science,10
English,7
I tried to create a class called Info and here is my code
public class Info
{
public string course { get; set; }
public int grade { get; set; }
public Info(string c, int g)
{
course = c;
grade = g;
}
internal IDictionary<string, Info> infoDic { get; private set; }
public void Set(string Sname, string course, int grade)
{
Student s = new Student(Sname);
var infor = new Info(course, grade);
infoDic = new Dictionary<string, Info>();
infoDic.Add(s.Name, infor);
//return infoDic;
}
public Dictionary<string, Info> retrieve (string name)
{
Student s = new Student(name);
return infoDic;
}
}
}
and here is another attempt:
I tried to make the class Info , and in the main I create the Dictionary and give the values, but the problem is I have lets say 3 courses and 10 students, and sometimes I need to retrieve only the math grades for all students.
How to improve the code to differentiate between the courses? Or how to make the course name as another key?
public class Info
{
public string course { get; set; }
public int grade { get; set; }
public Info(string c, int g)
{
course = c;
grade = g;
}
}
class Test
{
public static void Main(string[] args)
{
Dictionary<string, Info> information = new Dictionary<string, Info>();
Info i1 = new Info("math", 9);
information.Add("Student1", i1);
Info i2 = new Info("science", 11);
information.Add("Student1", i2);
Info i3 = new Info("math", 13);
information.Add("student2", i3);
foreach (KeyValuePair<string, Info> eee in information)
{
Console.WriteLine("{0}\t{1}\t{2}", eee.Key, eee.Value.type, eee.Value.count);
}
}
}
I need two methods one to set the values that user enters and the other to retrieve a certain course values when the user requires them
Is there Any suggestions?

Split the problem into separate concerns.
Use the Student and Info classes just to store data. Importantly, every Student holds a collection of his courses.
public class Student {
public Student(string name) {
Name = name;
Infos = new List<Info>();
}
public string Name {get; set;}
public ICollection<Info> Infos {get; set;}
}
public class Info {
public Info(string course, int grade) {
Course = course;
Grade = grade;
}
public string Course { get; set; }
public int Grade { get; set; }
}
Data access is handled by a different class StudentRepository.
The central dictionary is of type IDictionary<string, Student> with the Student name as key and hidden inside the repository.
using System.Linq;
public class StudentRepository {
public StudentRepository() {
_studentsByName = new Dictionary<string, Student>();
}
// keep data store private so we can change the implementation
private IDictionary<string, Student> _studentsByName {get; set;}
public void Add(Student student) {
if (_studentsByName.ContainsKey(student.Name)) {
throw new ArgumentException($"Student '{student.Name}' already stored.");
}
_studentsByName.Add(student.Name, student);
}
public Student Get(string studentName) {
if (_studentsByName.ContainsKey(studentName)) {
return _studentsByName[studentName];
}
throw new ArgumentException("No student '" + studentName + "' stored.");
}
// Find Grade for certain student and course
public int GetGrade(string studentName, string course) {
if (_studentsByName.ContainsKey(studentName)) {
var student = _studentsByName[studentName];
var courseInfo = student.Infos.FirstOrDefault(i => i.Course == course);
if (courseInfo != null) {
return courseInfo.Grade;
}
else {
throw new ArgumentException(
$"Student '{studentName}' did not take the course '{course}'.");
}
}
else {
throw new ArgumentException($"No student '{studentName}' found.");
}
}
// Get dictionary of all students that took a certain course. Key: student name
public IDictionary<string, Info> GetCoursesByStudentName(string course) {
// Use LINQ to retrieve the infos you need.
// Here I create a new dictionary with Student name as Key and
// the first matching course info found as value.
// (Students that did not take this course are not in this dictionary):
return _studentsByName
.Where(kvp => kvp.Value.Infos.Any(i => i.Course == course))
.ToDictionary(kvp => kvp.Key,
kvp => kvp.Value.Infos.First(i => i.Course == course));
}
}
Usage example:
const string MathCourseName = "Math";
var Student1 = new Student("Alice");
Student1.Infos.Add(new Info(MathCourseName, 4));
var Student2 = new Student("Bob");
Student2.Infos.Add(new Info(MathCourseName, 2));
var Student3 = new Student("Cesar");
Student3.Infos.Add(new Info("English", 3));
var repository = new StudentRepository();
repository.Add(Student1);
repository.Add(Student2);
repository.Add(Student3);
foreach(var kvp in repository.GetCoursesByStudentName(MathCourseName)) {
Console.WriteLine(kvp.Key + ": " + kvp.Value.Course + " - " + kvp.Value.Grade);
}
var bobsMathGrade = repository.GetGrade("Bob", MathCourseName);
Console.WriteLine("Bobs math grade: " + bobsMathGrade);
C# Fiddle for this example

What i understood from your statement, you need two methods: One for setting user values and other for getting user's course value, i've slightly modified your code in order to fulfill you requirement, you can do like this:
public class Info
{
public string course { get; set; }
public int grade { get; set; }
public Info(string c, int g)
{
course = c;
grade = g;
}
}
public class Student
{
public Dictionary<string,Dictionary<string,int>> student { get; set; }
public Student()
{
student = new Dictionary<string, Dictionary<string, int>>();
}
/// <summary>
///
/// </summary>
public void SetValue(string studentName, Info info)
{
if (!student.ContainsKey(studentName))
{
Dictionary<string, int> stud_info = new Dictionary<string, int>();
stud_info.Add(info.course, info.grade);
student.Add(studentName, stud_info);
}
else
{
student[studentName].Add(info.course, info.grade);
}
}
public Dictionary<string,int> GetValue(string studentName, string course)
{
Dictionary<string, int> info = new Dictionary<string, int>();
if (student.ContainsKey(studentName))
{
if (student[studentName].ContainsKey(course))
{
int grade = 0;
if(student[studentName].TryGetValue(course, out grade))
{
info.Add(course, grade);
return info;
}
}
}
return info;
}
}
public class Program
{
public static void Main(string[] args)
{
Student student = new Student();
Info i1 = new Info("math", 9);
student.SetValue("Student1", i1);
Info i2 = new Info("science", 11);
student.SetValue("Student1",i2);
Info i3 = new Info("math", 13);
student.SetValue("Student2", i3);
Dictionary<string, int> value = student.GetValue("Student2", "math");
//Grade of math for student2
Console.WriteLine("Grade: {0}", value["math"]);
}
}

Related

Error exporting to CSV when there are reference maps

I have s Student class where each student record has a list of Results.
I need to export there results to CSV and I'm using CsvHelper.
public class Student
{
public string Id { get; set; }
public string Name { get; set; }
public Result[] Grades { get; set; }
}
public class Result
{
public string Subject { get; set; }
public decimal? Marks { get; set; }
}
I'm using Reference Maps to map the list of Results, but when exporting to CSV it throws and error.
Mapping Code
public sealed class StudentResultExportMap : ClassMap<Student>
{
public StudentResultExportMap ()
{
AutoMap();
References<GradesMap>(m => m.Grades);
}
}
public sealed class GradesMap: ClassMap<Result>
{
public GradesMap()
{
Map(m => m.Subject);
Map(m => m.Marks);
}
}
Error
Property 'System.String Subject' is not defined for type
'{namespace}.GetStudentResults+Result[]' Parameter name: property
Unfortunately References<GradesMap>(m => m.Grades); doesn't work for an array of Result. It would work for an individual result. I have one solution, which overrides the ToString() method of Result to flatten the grades. It might work for you, depending on what you need.
public class Result
{
public string Subject { get; set; }
public decimal? Marks { get; set; }
public override string ToString()
{
return $"{Subject} = {Marks}";
}
}
Make a slight change to your StudentResultExportMap. You can set the 2nd number on .Index(2, 7) to handle the max number of grades you think a student might have.
public sealed class StudentResultExportMap : ClassMap<Student>
{
public StudentResultExportMap()
{
AutoMap();
Map(m => m.Grades).Name("Grade").Index(2, 7);
}
}
You will then get Id, Name, Grade1, Grade2, Grade3, Grade4, Grade5, Grade6 with the toString() value of Result for each grade.
var records = new List<Student>
{
new Student{ Id = "1", Name = "First", Grades = new [] {
new Result { Subject = "Subject1", Marks = (decimal)2.5 } ,
new Result { Subject = "Subject2", Marks = (decimal)3.5 } }},
new Student{ Id = "2", Name = "Second", Grades = new [] {
new Result { Subject = "Subject1", Marks = (decimal)3.5 } ,
new Result { Subject = "Subject2", Marks = (decimal)4.0 } }}
};
using (var writer = new StreamWriter("path\\to\\StudentResults.csv"))
using (var csv = new CsvWriter(writer))
{
csv.Configuration.RegisterClassMap<StudentResultExportMap>();
csv.WriteRecords(records);
}

Batch command with three items from textfile

I have a textfile, like this:
Tokyo
Japan
8797987
Amsterdam
Nederland
88788787
Den Haag
Nederland
787875454
Madrid
Spain
7877878
So in the batch there are three items:
Capital
Country
Population
and I am using the command Batch from MoreLinq.
I know to do it with a dictionary.
But how to manage this with three items for every batch?
What I mean with that is that for example you are searching for a capital and country that you will returned the capital + the country + the population
I try it like this:
public interface IDatabase
{
int GetPopulation(string name);
}
public class SingleTOnDatabase : IDatabase
{
private System.Collections.Generic.List capitols;
private SingleTOnDatabase()
{
Console.WriteLine("Initializing database");
capitols = File.ReadAllLines("Capitols.txt")
.Batch(3)
.ToList(
list => list.ElementAt(0).Trim(),
list => list.ElementAt(1).Trim(),
list => int.Parse((list.ElementAt(2)))
);
}
public int GetPopulation(string name)
{
return capitols[name];
}
private static Lazy<SingleTOnDatabase> instance = new Lazy<SingleTOnDatabase>(() => new SingleTOnDatabase());
public static SingleTOnDatabase Instance => instance.Value;
}
public class Program
{
static void Main(string[] args)
{
var db = SingleTOnDatabase.Instance;
var city = "Den Haag";
var Country = "Nederland";
Console.WriteLine($"{Country} with {city} has population of: {db.GetPopulation(city)}");
Console.Read();
}
}
You should never try to use text files as databases (if this is a serious job, for hobby projects who cares).
I revised your "batch" plus GetPopulation (and also added GetCapitol):
public interface IDatabase
{
int? GetPopulation(string name);
Capitol GetCapitol(string name);
}
public class Capitol
{
public string CapitolName { get; set; }
public string Country { get; set; }
public int? Population { get; set; }
}
public class SingleTOnDatabase : IDatabase
{
private System.Collections.Generic.List<Capitol> capitols;
private SingleTOnDatabase()
{
Console.WriteLine("Initializing database");
int pop;
capitols = (from batch in File.ReadAllLines("Capitols.txt").Batch(3)
let bArr = batch.ToArray()
where bArr.Length == 3
select new Capitol
{
CapitolName = bArr[0].Trim(),
Country = bArr[1].Trim(),
Population = int.TryParse(bArr[2], out pop) ? pop : (int?)null
}).ToList();
}
public int? GetPopulation(string name)
{
var capitol = GetCapitol(name);
return capitol?.Population;
}
public Capitol GetCapitol(string name)
{
return capitols.SingleOrDefault(c => c.CapitolName.ToLower().Trim() == name.ToLower().Trim());
}
private static Lazy<SingleTOnDatabase> instance = new Lazy<SingleTOnDatabase>(() => new SingleTOnDatabase());
public static SingleTOnDatabase Instance => instance.Value;
}
public class Program
{
static void Main(string[] args)
{
var db = SingleTOnDatabase.Instance;
var city = "Den Haag";
var Country = "Nederland";
Console.WriteLine($"{Country} with {city} has population of: {db.GetPopulation(city)}");
var city2 = "Tokyo";
var cap = db.GetCapitol(city2);
if (cap == null)
{
Console.WriteLine($"Unknown city [{city2}].");
}
else
{
Console.WriteLine($"{cap.CapitolName} is the capital of {cap.Country} and has population of: {cap.Population}");
}
Console.Read();
}
}
Note: With your given sample text at top, this is the output I get:
Initializing database
Nederland with Den Haag has population of: 787875454
Tokyo is the capital of Japan and has population of: 8797987

How to find a specific value in an IDictionary<string, object>?

This IDictionary<string, object> contains user data I'm logging into mongodb. The issue is the TValue is a complex object. The TKey is simply the class name.
For example:
public class UserData
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Admin NewAdmin { get; set; }
}
public class Admin
{
public string UserName { get; set; }
public string Password { get; set; }
}
Currently, I'm trying to iterate through the Dictionary and compare types but to no avail. Is there a better way of doing this or am I missing the mark?
var argList = new List<object>();
foreach(KeyValuePair<string, object> kvp in context.ActionArguments)
{
dynamic v = kvp.Value;
//..compare types...
}
Just use OfType<>(). You don't even need the key.
public static void Main()
{
var d = new Dictionary<string,object>
{
{ "string", "Foo" },
{ "int", 123 },
{ "MyComplexType", new MyComplexType { Text = "Bar" } }
};
var s = d.Values.OfType<string>().Single();
var i = d.Values.OfType<int>().Single();
var o = d.Values.OfType<MyComplexType>().Single();
Console.WriteLine(s);
Console.WriteLine(i);
Console.WriteLine(o.Text);
}
Output:
Foo
123
Bar
Link to Fiddle

Can we create a dictionary with in a list in C#?

I have to create a list as follows :
name, roll no
subjectno, subject type
subject no, subject type
e.g.
name[0] = ron, roll no[0] = 12
subjectno[0]=1, subject type[0]="english"
subjectno[1]=12, subject type[1]="maths"
name[1] = elis, roll no[1] = 11
subjectno[0]=1, subject type[0]="english"
subjectno[1]=12, subject type[1]="maths"
subjectno[2]=14, subject type[2]="physics"
I am not sure how to do this in C#.
I tried making a list of student info and then for subject no and subject type i tried to make a dictionary.
I have written the code like this -
class Student
{
public class StudentInfo
{
public String name { get; set; }
public int rollno { get; set; }
Dictionary<String, String> subjects;
public StudentInfo(String name, int rollno)
{
this.name = name;
this.rollno = rollno;
}
public void addstudentinfo(string subjectno, string subjecttype)
{
if (subjects == null)
subjects = new Dictionary<string, string>();
subjects.Add(subjectno, subjecttype);
}
}
Here how I would do. First create these two class
public class Student
{
public int RollNo { get; set; }
public string Name { get; set; }
public Student(int rNo, string name)
{
this.RollNo = rNo;
this.Name = name;
}
public Student()
{
}
}
public class Subject
{
public int SubjectNo { get; set; }
public string Type { get; set; }
public Subject(int sNo, string sType)
{
this.SubjectNo = sNo;
this.Type = sType;
}
public Subject()
{
}
}
Then fill in the objects as follows :-
Dictionary<Student, List<Subject>> studentLists = new Dictionary<Student, List<Subject>>();
Student std = new Student() { RollNo = 11, Name = "John" };
List<Subject> sbj = new List<Subject>() {new Subject(020, "Math"),new Subject(030,"English") };
studentLists.Add(std, sbj);
Then iterate thru the Dictionary as follows:-
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<Student, List<Subject>> item in studentLists)
{
sb.Append("Student No : " + item.Key.RollNo + "<br />");
sb.Append("Student Name : " + item.Key.Name + "<br />");
foreach (var subjects in item.Value)
{
sb.Append("Subject No : " + subjects.SubjectNo + "<br />");
sb.Append("Subject Name : " + subjects.Type + "<br />");
}
}
Hope this helps.
Your requirements aren't clear, but it looks best to create a Subject class as well.
class Subject{
public int Id {get;set;}
public string Type {get;set;}
}
From there, you can change Dictionary<String, String> subjects to a simple List<Subject> type. And change addstudentinfo method to take a Subject as a parameter.
You can further improve it by ensuring that only one instance of a particular Subject ever exists by modifying the Subject class (For example, to use the Creator pattern).
You can create struct or class to contain student's info and than add it to Dictionary<string, StudInfo>, where string is student name as Key.
Class for storing student info:
public class StudInfo
{
public int RollNum;
public List<string> Activities = new List<string>();
public StudInfo(int num)
{
RollNum = num;
}
}
And its usage:
var dict = new Dictionary(string, StudInfo);
var info = new StudInfo(12);
dict.Add("Ellis", info);

Storing in a nested dictionary C#

Broadcom
Connection Name: Local Area Connection
DHCP Enabled: No
IP address(es)
[01]: abc.de.fg.h
I would like to put this into a Dictionary<string, Dictionary <string, string>>
So Broadcom would be the key in the first dictionary and the rest would be value the value. The value should be stored as a dictionary where Connection Name is the key and Local Area Connection is the value. How do I go about doing that? I am new to programming. Thanks for the help.
Please do yourself a great favour and use a class for that, because in two months time you will not understand your code:
public class WhatIsThis {
private List<IPAddress> ipAddresses = new List<IPAddress>();
public string Name { get; set; }
public string ConnectionName { get; set; }
public bool DHCPEnabled { get; set; }
public List<IPAddress> IPAddresses { get return ipAddresses; }
}
Now you can keep a List<WhatIsThis> around. If you have justa few of them, the O(n) cost of looking a value up is negligible. If you want a lookup by name in O(1) fashion, you can map to a Dictionary as such:
var dict = listOfWhatisThis.ToDictionary(k=>k.Name, v=>v}
Gives you a Dictionary of type Dictionary<string,WhatIsThis> . What do you think, will you understand this better in 2 weeks time, or rather your scary Dictionary?
http://msdn.microsoft.com/en-us/library/bb531208.aspx
var dic = new dictionary<string, dictionary <string, string>>(
{"Broadcom",
new dictionary <string, string>(
{
{"Connection Name", "Local Area Connection"},
{"DHCP Enabled", "No"},
{"IP address(es) [01]", "abc.de.fg.h"}
}
)
}
);
My only concern would be that the IP Addresses should themselves be in a dictionary within the settings for a connection, not a single string, and thus the sub-dictionary would need to be dictionary<string, object>.
Similar to flq's answer, here's a more comprehensive solution which makes both subdictionaries:
using System.Collections.Generic;
using System.Linq;
namespace LINQDictionaryDemo
{
public class IPAddress
{
public int Index { get; private set; }
public string Value { get; private set; }
public IPAddress(int Index, string Value)
{
this.Index = Index;
this.Value = Value;
}
}
public class NetworkConnection
{
public string Name { get; set; }
public string ConnectionName { get; set; }
public bool DHCPEnabled { get; set; }
public List<IPAddress> IPAddresses { get; set; }
public Dictionary<string, object> ToDictionary()
{
return new Dictionary<string, object>
{
{ "ConnectionName", ConnectionName }
, { "DHCPEnabled", DHCPEnabled.ToString() }
, {"IPAddresses", IPAddresses.ToDictionary(k => k.Index, v => v.Value)}
};
}
}
public static class Demo
{
public static void Run()
{
var lnc = new List<NetworkConnection>
{
new NetworkConnection
{
Name = "Broadcom",
ConnectionName = "Local Area Connection",
DHCPEnabled = false,
IPAddresses = new List<IPAddress> {new IPAddress(1, "abc.de.fg.h")}
}
};
var dic = lnc.ToDictionary(k => k.Name, v => v.ToDictionary());
}
}
}

Categories

Resources