ListCollectionView grouping already grouped items - c#

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();

Related

How to get a value from dicitionary in 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.

Update list of items in c#

I would like to know if you can suggest me an efficient way to update a list of items in c#. Here is a generic example:
If CurrentList is
[ {Id: 154, Name: "George", Salary: 10 000}
{Id: 233, Name: "Alice", Salary: 10 000}]
And NewList is
[ {Id: 154, Name: "George", Salary: 25 000}
{Id: 234, Name: "Bob", Salary: 10 000}]
Then the result should be:
[{Id: 154, Name: "George", Salary: 25 000}
{Id: 234, Name: "Bob", Salary: 10 000} ]
I don't want just to clear the first one and use the values from the second one, but want to update the ones with the same ID, remove the ones that have been deleted and add any new ones.
Thanks in advance.
I would do something like this: (for ordinairy lists)
// the current list
var currentList = new List<Employee>();
currentList.Add(new Employee { Id = 154, Name = "George", Salary = 10000 });
currentList.Add(new Employee { Id = 233, Name = "Alice", Salary = 10000 });
// new list
var newList = new List<Employee>();
newList.Add(new Employee { Id = 154, Name = "George", Salary = 25000 });
newList.Add(new Employee { Id = 234, Name = "Bob", Salary = 10000 });
// clean up
foreach (var oldEmployee in currentList.ToArray())
if (!newList.Any(item => oldEmployee.Id == item.Id))
currentList.Remove(oldEmployee);
// check if the new item is found within the currentlist.
// If so? update it's values else add the object.
foreach (var newEmployee in newList)
{
var oldEmployee = currentList.FirstOrDefault(item => item.Id == newEmployee.Id);
if (oldEmployee == null)
{
// add
currentList.Add(newEmployee);
}
else
{
// modify
oldEmployee.Name = newEmployee.Name;
oldEmployee.Salary = newEmployee.Salary;
}
}
You can speed it up, using dictionaries, but that's not your question (for now)
You can do it with use of for loop and Linq expression:
for (int i = 0; i < NewList.Count; i++)
{
var record = CurrentList.FirstOrDefault(item => item.Id == NewList[i].Id);
if (record == null) { CurrentList.Add(NewList[i]); }
else { record.Id = NewList[i].Id; record.Name = NewList[i].Name; record.Salary = NewList[i].Salary; }
}
CurrentList.RemoveAll(item => NewList.FirstOrDefault(item2 => item2.Id == item.Id) == null);
Example of usage:
Example
A LINQ'y version wrapped in an extension method, could modified to be generic if 'Id' is on a interface of some sort.
The merge Action could potentially be a Merge() method on entity objects such as employee but I chose to use a delegate here .
public class Tests
{
[Test]
public void MergeSpike()
{
// the current list
var currentList = new List<Employee>();
currentList.Add(new Employee { Id = 154, Name = "George", Salary = 10000 });
currentList.Add(new Employee { Id = 233, Name = "Alice", Salary = 10000 });
// new list
var newList = new List<Employee>();
newList.Add(new Employee { Id = 154, Name = "George", Salary = 25000 });
newList.Add(new Employee { Id = 234, Name = "Bob", Salary = 30000 });
currentList.Merge(newList, (o, n) =>
{
if(o.Id != n.Id) throw new ArgumentOutOfRangeException("Attempt to merge on mismatched IDs");
o.Name = n.Name;
o.Salary = n.Salary;
});
Assert.That(currentList.Count(), Is.EqualTo(2));
Assert.That(currentList.First(c => c.Id == 154).Salary, Is.EqualTo(25000));
Assert.That(currentList.Any(c => c.Id == 233), Is.False);
Assert.That(currentList.First(c => c.Id == 234).Salary, Is.EqualTo(30000));
}
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
}
public static class EmployeeListExtensions
{
public static void Merge(this List<Employee> currentList, IEnumerable<Employee> newList, Action<Employee, Employee> merge)
{
// Updates
currentList.Where(e => newList.Any(n => n.Id == e.Id))
.ToList().ForEach(e => merge(e, newList.First(n1 => n1.Id == e.Id)));
// Deletes
var remove = currentList.Where(cl => newList.All(nl => cl.Id != nl.Id)).ToList();
currentList.RemoveAll(e => remove.Any(r => r.Id == e.Id));
// Inserts
currentList.AddRange(newList.Where(nl => currentList.Any(c => c.Id != nl.Id)));
}
}

Descending sorting of a list of object based on a count from a different class

I'm stuck on this problem where I need to do descending sort based on other list. l_lstNames need to update by age descending.
public class Test
{
public String Name;
public Int32 Age;
}
List<String> l_lstNames = new List<String> { "A1", "A3", "A2", "A4", "A0" };
List<Test> l_lstStudents = new List<Test>
{
new Test { Age = 33, Name = "A0" },
new Test { Age = 10, Name = "A1" },
new Test { Age = 50, Name = "A2" },
new Test { Age = 8, Name = "A3" },
new Test { Age = 25, Name = "A4" },
};
// Output
List<String> l_lstNames = new List<String> { "A2", "A0", "A4", "A1", "A3" };
Found few sames samples but not matching what I'm looking for. Thank you for help.
Create Dictionary<string, int> with Name to Age mapping and use it within order method:
var dict = students.ToDictionary(x => x.Name, x => x.Age);
var ordered = source.OrderByDescending(x => dict[x.Name]).ToList();
or you can just order students collection and then select Name only:
var ordered = students.OrderByDescending(x => x.Age)
.Select(x => x.Name)
.ToList();
If you just want the names in order descending:
var sorted = l_lstStudents // From the list of students
.OrderByDescending(l => l.Age) // with the oldest student first
.Select(s => s.Name) // give me just the names
.ToList(); // in a list!
I think this is what you are looking for
List<String> l_lstNames1 = (from student in l_lstStudents
where l_lstNames.Any(a => student.Name == a)
orderby student.Age descending
select student.Name ).ToList();
OR
List<String> l_lstNames2 = l_lstStudents.OrderByDescending(a => a.Age)
.Where(a => l_lstNames.Any(b => b == a.Name))
.Select(a => a.Name).ToList();

Remove duplicate items and calculate average values using LINQ

For example I have a list of objects (properties: Name and value)
item1 20;
item2 30;
item1 50;
I want the result:
item1 35 (20+50)/2
item2 30
How can I do this?
Sorry guys, duplicate is based on item.Name.
var results =
from kvp in source
group kvp by kvp.Key.ToUpper() into g
select new
{
Key= g.Key,
Value= g.Average(kvp => kvp.Value)
}
or
var results = source.GroupBy(c=>c.Name)
.Select(c => new (c.Key, c.Average(d=>d.Value)));
You could do it using average and group by:
public class myObject
{
public string Name {get;set;}
public double Value {get;set;}
}
var testData = new List<myObject>() {
new myObject() { Name = "item1", Value = 20 },
new myObject() { Name = "item2", Value = 30 },
new myObject() { Name = "item1", Value = 50 }
};
var result = from x in testData
group x by x.Name into grp
select new myObject() {
Name=grp.Key,
Value= grp.Average(obj => obj.Value)
};

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