How to get a value from dicitionary in c# - c#

static void Main(string[] args)
{
List<People> people = new List<People>(){
new People(){FirstName = "aaa", LastName = "zzz", Age = 3, Location = "Berlin"},
new People(){FirstName = "aaa", LastName = "yyy", Age = 6, Location = "Paris"},
new People(){FirstName = "bbb", LastName = "zzz", Age = 5, Location = "Texas"},
new People(){FirstName = "bbb", LastName = "yyy", Age = 4, Location = "Sydney"},
new People(){FirstName = "ccc", LastName = "zzz", Age = 2, Location = "Berlin"},
new People(){FirstName = "ccc", LastName = "yyy", Age = 3, Location = "New York"},
new People(){FirstName = "aaa", LastName = "xxx", Age = 2, Location = "Dallas"},
new People(){FirstName = "bbb", LastName = "www", Age = 6, Location = "DC"},
new People(){FirstName = "ccc", LastName = "vvv", Age = 3, Location = "Detroit"},
new People(){FirstName = "ddd", LastName = "uuu", Age = 5, Location = "Gotham"}
};
var dict = people
.GroupBy(x => (x.FirstName, x.LastName))
.ToDictionary(x => x.Key,
x => x.ToList());
/**
how to get a value from dictionary when i just have first name.
i want to get all value from dict where name = "aaa"
**/
}
public class People
{
public string FirstName {get; set;}
public string LastName {get; set;}
public int Age {get; set;}
public string Location {get; set;}
}
is there a way to get a value from dictionary with just 1 key (example i just have name "aaa", and i want to get all people with Firstname "aaa"). i can get it with where but there is no point in using dictionary. should i used nested dictionary or there's other way ?

There is no point using the dictionary, I'm not sure what you're trying to achive by using it? Why not just use the list as mcjmzn said?
List<people> peopleCohort = people.Where(p=> p.FirstName == "aaa").ToList();
Think you might be overthinking it.
Stu.
UPDATE:
Given the following test:
SqlCommand com = new SqlCommand("SELECT Name FROM [Responses_PersonalData]", con);
con.Open();
List<Person> listPeople = new List<Person>();
Dictionary<string, Person> dicPeople = new Dictionary<string, Person>();
using (con)
{
Random rand = new Random();
using (SqlDataReader reader = com.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
//Only use data where we have firstname, surname, approx 49,000 names in db.
string[] name = reader["Name"].ToString().Trim().Split(' ');
if (name.Length == 2)
{
Person person = new Person() { Age = rand.Next(0, 100), FirstName = name[0], LastName = name[1], Location = name[1] };
listPeople.Add(person);
}
}
}
}
}
//Creates approx 100 million people exponentially.
for (int i = 1; i < 12; i++)
listPeople.AddRange(listPeople);
//Group by firstname lastname tuple
var tuppleDicPeople = listPeople
.GroupBy(x => (x.FirstName, x.LastName))
.ToDictionary(x => x.Key,
x => x.ToList());
//Method 1
List<Person> listPeopleCohortResults = listPeople.FindAll(p => p.FirstName == "Dean");
//Method 2
List<Person> dicPeopleCohortResults = tuppleDicPeople.Where(kvp => kvp.Key.FirstName == "Dean").SelectMany(kvp => kvp.Value).ToList();
Findings:
The group by operation is very expensive.
listPeople.FindAll(p => p.FirstName == "Dean"); => 1651ms, returns 32768 results.
List dicPeopleCohortResults = tuppleDicPeople.Where(kvp => kvp.Key.FirstName == "Dean").SelectMany(kvp => kvp.Value).ToList(); => 10ms, returns 32768 results.
If you can afford the expense of the group by then your solution is optimal given the limited research I've done.
Stu.

Related

Best average student(s) score (C#, LINQ, without loops)

Can I somehow calculate the average for different items and choose student(s) with best GPA?
public static List<Student> LoadSampleData()
{
List<Student> output = new List<Student>();
output.Add(new Student { ID = 1, FirstName = "Tim", LastName = "Corey ", Patronymic = "Fitzpatrick ", Group = "A", Math = 5, Programming = 5, Informatics = 5});
output.Add(new Student { ID = 2, FirstName = "Joe", LastName = "Smith ", Patronymic = "Mackenzie ", Group = "A", Math = 3, Programming = 3, Informatics = 4});
output.Add(new Student { ID = 3, FirstName = "Ellie", LastName = "Williams ", Patronymic = "", Group = "B", Math = 4, Programming = 5, Informatics = 4});
output.Add(new Student { ID = 4, FirstName = "Joel", LastName = "Miller ", Patronymic = "", Group = "B", Math = 4, Programming = 4, Informatics = 5});
return output;
}
I need it to be calculated approximately according to the following logic (finding the average for all subjects for each student. For example: student_avarage(Math+Programming+Informatics) and find the best score). Without using loops like: for, while, if and etc. ("foreach{}" too)
public static void BestStudentsAvarage()
{
List<Student> students = ListManager.LoadSampleData();
var StudentAverage =
from student in students
group student by student.ID into studentID
select new
{
ID = studentID.Key,
student_Average = studentID.Average(x => x.(Math+Programming+Informatics))
};
var bestGrade = StudentAverage.Max(gr => gr.student_Average);
var bestIDs_1 = StudentAverage.Where(g => g.student_Average == bestGrade);
var bestID_1 = bestIDs_1.FirstOrDefault();
Console.WriteLine($"\nBest student(s) GPA: {bestID_1.ID} \nScore: {bestID_1.student_Average}");
Console.ReadLine();
}
I think this is what you actually want(divide the sum of the three subjects through 3):
public static List<(Student student, decimal average)> BestStudentsAvarage(List<Student> students)
{
return students
.Select(s => (Student:s,Average:(s.Math+s.Programming+s.Informatics)/3m))
.GroupBy(g => g.Average)
.OrderByDescending(g => g.Key)
.First()
.ToList();
}
List<Student> sample = LoadSampleData();
List<(Student student, decimal average)> bestAvarageStudents = BestStudentsAvarage(sample);
foreach(var x in bestAvarageStudents)
{
Console.WriteLine($"Best student <{x.student.FirstName} {x.student.LastName}> with Average <{x.average}>");
}
With your example it would output: Best student <Tim Corey> with Average <5>

Lambda expression groupby on single column and fetch all column orderby count

Here my query is I have a list with repeated data and on that, I want to do groupby clause on FName column and display in order by descending of a count and display all record of that particular list
List<Employee> empList = new List<Employee>();
empList.Add(new Employee() { ID = 1, FName = "John", Age = 23, Sex = 'M' });
empList.Add(new Employee() { ID = 2, FName = "Mary", Age = 25, Sex = 'F' });
empList.Add(new Employee() { ID = 3, FName = "John", Age = 28, Sex = 'M' });
empList.Add(new Employee() { ID = 4, FName = "Amber", Age = 23, Sex = 'M' });
empList.Add(new Employee() { ID = 5, FName = "Kathy", Age = 25, Sex = 'M' });
empList.Add(new Employee() { ID = 6, FName = "Lena", Age = 27, Sex = 'F' });
empList.Add(new Employee() { ID = 7, FName = "John", Age = 28, Sex = 'M' });
empList.Add(new Employee() { ID = 8, FName = "Kathy", Age = 27, Sex = 'F' });
var dup1 = empList
.GroupBy(x => new { x.FName })
.Select(group => new { Name = group.Key.FName, Count = group.Count() })
.OrderByDescending(x => x.Count);
foreach (var x in dup1)
{
Console.WriteLine(x.Count + " " + x.Name);
}
From the above code I am getting output like this:
But what i actually want is like this:
It looks like you want the group count, and then the information about the first item in the group. If that's the case, then you can simply use GroupBy to group the items, and then in your output just capture and display the information for the first item in the group:
var groups = empList.GroupBy(e => e.FName).OrderByDescending(group => group.Count());
foreach (var group in groups)
{
var first = group.First();
Console.WriteLine($"{group.Count()} {first.FName}\t{first.Age} {first.Sex}");
}
Output
.Select(group => new
{
Name = group.Key.FName,
Count = group.Count(),
Age = group.First().Age,
Sex = group.First().Gender
});

ListCollectionView grouping already grouped items

How can i make double grouping in ListCollectionView(by double grouping i mean not just grouping by two properties, but group what is already grouped by another property)?
example:
class Person{
public string Name;
public int Age;
{
List<Person> list = new List<Person>{
new Person{ Name = Alex, Age = 22 },
new Person{ Name = Alex, Age = 23 },
new Person{ Name = Sam, Age = 19 },
new Person{ Name = Sam, Age = 33 }
};
ListCollectionView listView = new ListCollectionView(list);
listView.GroupDescriptions.add(new PropertyGroupDescription("Name"));
that is all i have right now, is there some way?
Alex:
22:
Alex 22;
23:
Alex 23;
Sam:
19:
Sam 19;
33:
Sam 33;
Thank in advance for your help!
var result= list.GroupBy(item => item.Name)
.Select(group => new
{
Name = group.Key,
Values = group.GroupBy(item => item.Age)
.Select(innerGroup => new
{
Age = group.Key,
Values = group.ToList()
}).ToList()
}).ToList();

AsParallel - Before vs After in Linq Where Clause Performance

I'm having a List<Boss> Collection, every Boss has 2 to 10 Assistant Staff. I'm grouping all the Employees including Boss. Now I'm having List<Person>, from this I'm searching "Raj" using Parallel LINQ, where can I place the supportive method AsParallel() to get better performance, Before or After the Where Clause ?
public class Person
{
public int EmpID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
}
void Main()
{
List<Boss> BossList = new List<Boss>()
{
new Boss()
{
EmpID = 101,
Name = "Harry",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 102, Name = "Peter", Department = "Development",Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development",Gender = "Female"},
}
},
new Boss()
{
EmpID = 104,
Name = "Raj",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 105, Name = "Kaliya", Department = "Development",Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development",Gender = "Female"},
}
}
};
List<Person> result = BossList
.SelectMany(x =>
new[] { new Person { Name = x.Name, Department = x.Department, Gender = x.Gender, EmpID = x.EmpID } }
.Concat(x.Employees))
.GroupBy(x => x.EmpID) //Group by employee ID
.Select(g => g.First()) //And select a single instance for each unique employee
.ToList();
List<Person> SelectedResult = new List<Person>();
// AsParallel() - Before Where Clause
SelectedResult = result.AsParallel().Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant())).ToList();
// AsParallel() - After Where Clause
SelectedResult = result.Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant())).AsParallel().ToList();
}
Core Source Code:
List<Person> SelectedResult = new List<Person>();
// AsParallel() - Before Where Clause
SelectedResult = result.AsParallel().Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant())).ToList();
// AsParallel() - After Where Clause
SelectedResult = result.Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant())).AsParallel().ToList();
Before.
AsParallel helps us to run queries in parallel, which is enabling parallel threads to improve performance. If you put before WHERE clause the filtering will be done in series, and only then will anything be parallelized.
Here is some test code:
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
class AsParallelTest
{
static void Main()
{
var query = Enumerable.Range(0, 1000)
.Select(ProjectionExample)
.Where(x => x > 10)
.AsParallel();
Stopwatch stopWatch = Stopwatch.StartNew();
int count = query.Count();
stopWatch.Stop();
Console.WriteLine("Count: {0} in {1}ms", count,
stopWatch.ElapsedMilliseconds);
query = Enumerable.Range(0, 1000)
.AsParallel()
.Select(ProjectionExample)
.Where(x => x > 10);
stopWatch = Stopwatch.StartNew();
count = query.Count();
stopWatch.Stop();
Console.WriteLine("Count: {0} in {1}ms", count,
stopWatch.ElapsedMilliseconds);
}
static int ProjectionExample(int arg)
{
Thread.Sleep(10);
return arg;
}
}
Result:
Count: 989 in 10574ms
Count: 989 in 1409ms
It's obvious that the first result hasn't been parallelized, where the second has. If you have only one processor core, the results should be close. If you have more than two processor cores, the AsParallel call may increase performance even more.
Also, you can read this article.

C# linq group by

How do I count, group and sort the following list based on a persons money with linq?
Person[] names = { new Person{ Name = "Harris", Money = 100 },
new Person{ Name = "David", Money = 100 },
new Person{Name = "Harris", Money = 150},
new Person{Name = "Mike", Money = 100},
new Person{Name = "Mike", Money = 30},
new Person{Name = "Mike", Money = 20} };
The result would return:
Harris 250
Mike 150
David 100
var personMoney = names.GroupBy(x=>x.Name)
.Select(x=>new {Name = x.Key, AllMoney = x.Sum(y=>y.Money)})
.OrderByDescending(x=>x.AllMoney).ToList();
from p in names
group p by p.Name into g
order by g.Key
select new { Name = g.Key, Amount = g.Sum(o => o.Amount) }

Categories