New dictionary using LINQ to dictionary - c#

I have Dictionary<Position, List<GameObject>> and I want to create new dictionary Dictionary<Position, List<Person>> which collect positions which including at least one person in the List<GameObject>.
I tried:
Dictionary<Position, List<Person>> persons =
positions.ToDictionary(
i => i.Key,
i => i.Value.Where(obj => obj is Person))
positions is the first dictionary.
but it doesn't really work because the position sometimes is null and its not really type of Person because you don't convert it. anyway, do you have any ideas?

var people = positions.Select(x => new { x.Key, Values = x.Value.OfType<Person>().ToList() })
.Where(x => x.Values.Any())
.ToDictionary(x => x.Key, x => x.Values)

Related

C# LINQ: Can we return same instance from GroupBy instead of doing new?

I have a collection where I need to group and find max from that group. So I did
var foo = foobar.GroupBy(x => x.Name)
.Select(x => new Foo { Name = x.Key, Version = x.Max(v => v.Version)))
.ToList();
If there are say more that 2 properties, is it possible to return same object instead of creating new?
Sure, use OrderByDescending on the group and then First to get the max-version-object:
var maxVersionObjectByName = foobar
.GroupBy(x => x.Name)
.Select(grp => grp.OrderByDescending(x => x.Version).First())
.ToList();

How to declare the return Type of a grouping Linq query

What is the type that the following query returns:
var photos = job.Photos.GroupBy(x => x.Location)
.Select(group =>
new
{
Name = group.Key,
Photos = group.OrderByDescending(x => x.DateAdded)
})
.OrderBy(group => group.Name)
.ToList();
According to the debugger it is of type:
'System.Collections.Generic.List<<anonymous type: string Name, System.Linq.IOrderedEnumerable<Project.Models.Photo> Photos>>'
but I don't know how to declare this type.
I've tried
public List<string, IOrderedEnumerable<Photo>> Photos {get; set;)
But List can only take one element
Use Dictionary to hold the string key and list of photos as values, if you don't want to create another class, just to hold this information.
IDictionary<string, List<Photo>> photos = job.Photos.GroupBy(x => x.Location)
.Select(group =>
new
{
Name = group.Key,
Photos = group.OrderByDescending(x => x.DateAdded)
})
.OrderBy(group => group.Name)
.ToDictionary(kv=>kv.Name, kv=>kv.Photos.ToList());
You have couple options:
you could create your class to hold both the grouping key and the collection of values associated with it. And create an instance of that class instead of anonymous types.
you could use Dictionary<TKey, TValue>with Valuebeing IOrderedEnumerable<T>
var photos = job.Photos
.GroupBy(x => x.Location)
.ToDictionary(group => group.Key,
group => group.OrderByDescending(x => x.DateAdded));
I'd add an extra ToList call after OrderByDescending to prevent for the items to be resolved and sorted just once.
As the debugger says, it's an anonymous type. That means that you can't write its name, because it doesn't have one. What you can do is to create a new class and use that in your query:
List<PhotosGroup> photos = job.Photos.GroupBy(x => x.Location)
.Select(group =>
new PhotosGroup
{
Name = group.Key,
Photos = group.OrderByDescending(x => x.DateAdded)
})
.OrderBy(group => group.Name)
.ToList();
Or you could use the Tuple type:
List<Tuple<string, IOrderedEnumerable<Photo>> photos = job.Photos.GroupBy(x => x.Location)
.Select(group =>
Tuple.Create(
group.Key,
group.OrderByDescending(x => x.DateAdded)))
.OrderBy(group => group.Item1)
.ToList();

Create a C# dictionary from DB query with repeated keys

I have a Database with schools, and each school is in a City.
Now I want to create a dictionary that contains all the cities of each schoool. To achieve this I tried this approach:
var schoolCities = schoolsWithAddresses.Where(school => school.Address.City != null).ToDictionary(sc => sc.Address.City.Name.ToLower());
Now, the problem with this is that a City can have multiple schools. So, when I create my dictionary, I end up with an exception "Repeated Key".
I want to create a dicitonary because it will allow me to make a very quick lookup on the cities that have schools (that is why I am not using a List, for example).
How do I overcome this problem in a way rhat allows me to still make efficient lookups?
Use the ToLookUp extension method rather
var schoolCities = schoolsWithAddresses
.Where(school => school.Address.City != null)
.ToLookup(sc => sc.Address.City.Name.ToLower());
You should group the items first, so that you have unique cities.
schoolsWithAddresses.Where(school => school.Address.City != null)
.GroupBy(s => s.Address.City, (k, v) => new { City = k, Schools = v })
.ToDictionary(d => d.City, e => e.Schools)
;
Something like this:
Dictionary<string, List<School>> schoolCities = schoolsWithAddresses
.Where(school => school.Address.City != null)
.GroupBy(school => school.Address.City)
.ToDictionary(group => group.Key, group => group.ToList());
I think what you want is a Lookup:
Represents a collection of keys each mapped to one or more values.
Example usage:
Lookup<string, School> schoolCities = schoolsWithAddresses
.Where(school => school.Address.City != null)
.ToLookup(school => school.Address.City);
IEnumerable<School> schoolsInLondon = schoolCities["London"];
try this
var schoolCities = schoolsWithAddresses
.GroupBy(x => x.city!=null)
.ToDictionary(x => x.Key, x => x.ToList());

Linq to Entities group query giving list of results in each group

If I have a set of entities with 3 properties (Id, Type, Size) all of which are strings.
Is there a way using Linq to Entities where I can do a group query which gives me the Size + Type as the key and then a list of the related Id's for that Size + Type?
Example below of getting the count:
Items.GroupBy(x => new { x.Size, x.Type})
.Select(x => new { Key = x.Key, Count = x.Count() })
but I am looking to get a list of the Ids for each grouping?
I am looking to see if it is possible using Linq-to-EF before I decide to iterate through this in code and build up the result instead.
If you want to get List of Ids for each group then you have to select x.Select(r => r.Id) like:
var result = Items.GroupBy(x => new { x.Size, x.Type })
.Select(x => new
{
Key = x.Key,
Ids = x.Select(r => r.Id)
});
Another way to build up a Dictionary<string, IEnumerable<string?>> in dotnet 6.0 according to the docs;
where we have the dictionary Key as {Size, Type} and Value the list of Ids, you can write:
Dictionary<string, IEnumerable<string?>> result = Items.GroupBy(item => new { item.Size, item.Type }
item => item.Id),
(itemKey, itemIds) =>
{
Key = itemKey,
Ids = itemIds
})
.ToDictionary(x => x.Key, x=> x.Ids);

Dictionaries: An item with the same key has already been added

In my MVC app I am using 2 dictionaries to populate SelectList for DropDownList. Those dictionaries will be supplied with dates as string and datetime values.
I have this chunk of code for the first dictionary that works just fine:
if (m_DictDateOrder.Count == 0)
{
m_DictDateOrder = new Dictionary<string, DateTime>();
m_DictDateOrder =
m_OrderManager.ListOrders()
.OrderBy(x => x.m_OrderDate)
.Distinct()
.ToDictionary(x => x.m_OrderDate.ToString(), x => x.m_OrderDate);
}
But when I get to the second dictionary:
if (m_DictDateShipped.Count == 0)
{
m_DictDateShipped = new Dictionary<string, DateTime>();
m_DictDateShipped =
m_OrderManager.ListOrders()
.OrderBy(x => x.m_ShippedDate)
.Distinct()
.ToDictionary(x => x.m_ShippedDate.ToString(), x => x.m_ShippedDate);
}
I get a runtime error on the LINQ request for the second dictionary:
An item with the same key has already been added.
I first though that I add to instantiate a new dictionary (that's the reason for the "new" presence), but nope. What did I do wrong?
Thanks a lot!
You are Distinct'ing the rows, not the dates.
Do this instead:
if (m_DictDateShipped.Count == 0)
{
m_DictDateShipped = m_OrderManager.ListOrders()
//make the subject of the query into the thing we want Distinct'd.
.Select(x => x.m_ShippedDate)
.Distinct()
.ToDictionary(d => d.ToString(), d => d);
}
Don't bother sorting. Dictionary is unordered.
My standard pattern for this (since I have disdain for Distinct) is:
dictionary = source
.GroupBy(row => row.KeyProperty)
.ToDictionary(g => g.Key, g => g.First()); //choose an element of the group as the value.
You applied the Distinct to the order, not to the date. Try
m_OrderManager.ListOrders()
.OrderBy(x => x.m_ShippedDate)
.Select(x =>x.m_ShippedDate)
.Distinct()
.ToDictionary(x => x.ToString(), x => x);

Categories

Resources