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);
Related
need help to only select/get distinct entries based on i.Code.
There are duplicates and thus I'm getting an error in my expression "An item with the same key has already been added."
var myDictionary = dbContext.myDbTable
.Where(i => i.shoesize>= 4)
.OrderBy(i => i.Code)
.ToDictionary(i => i.Code, i => i);
Have tried to use Select and/or Distinct in different combinations and also by themselves but am still getting the same error
var myDictionary= dbContext.myDbTable
.Where(i => i.shoesize>= 4)
.OrderBy(i => i.Code)
//.Select(i => i)
//.Distinct()
.ToDictionary(i => i.Code, i => i);
Can anybody help? C#
UPDATE: If there are multiple objects with the same code I only want to add the first object(with that particular code) to myDictionary.
You can group by Code and select the first item from each group (which is equivalent to distinct):
var myDictionary = dbContext.myDbTable
.Where(i => i.shoesize >= 4) // filter
.GroupBy(x => x.Code) // group by Code
.Select(g => g.First()) // select 1st item from each group
.ToDictionary(i => i.Code, i => i);
You don't need the OrderBy since Dictionarys represent an unordered collection. If you need an ordered dictionary you could use SortedDictionary.
It sounds to me that what you are looking for is .DistinctBy() (available in .NET 6), which lets you specify which property to distinct the elements in your collection by:
var myDictionary= dbContext.myDbTable
.Where(i => i.shoesize>= 4)
.DistinctBy(i => i.Code)
.ToDictionary(i => i.Code, i => i);
By dividing it and creating a list first it worked as compared to when it was all bundled up into one linq, guess the First() needed it to be in a list before being able to make it into a dict.
var firstLinq = dbContext.myDbTable
.Where(i => i.shoesize>= 4)
.ToList();
then
var finalLinq = fromConcurWithDuplicates
.GroupBy(i => i.Code)
.Select(i => i.First())
.ToList()
.ToDictionary(i => i.Code, i => i);
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());
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have the following code:
string[] tokens; //columns which I want to groupBy (e.g., storeID, location)
Dictionary<string, List<DataRow>> dictShort = null; // what I want to return
switch (tokens.Length)
{
case 1:
dictShort = dsShort.Tables[0].AsEnumerable()
.GroupBy(x => x[block])
.Where(g => exp.GroupSizeOk((uint)g.Count()))
.OrderBy(g => g.Count())
.ToDictionary(g => g.Key.ToString(), g => g.ToList());
break;
case 2:
dictShort = (Dictionary<string, List<DataRow>>)
dsShort.Tables[0].AsEnumerable()
.GroupBy(x => x[tokens[0]], x => x[tokens[1]])
.Where(g => exp.GroupSizeOk((uint)g.Count()))
.OrderBy(g => g.Count())
.ToDictionary(g => g.Key.ToString(), g => g.ToList());
// NOT COMPILING> cannot convert Dictionary<string, List<objet>>
// to Dictionary<string, List<DataRow>>
break;
case 3:
dictShort = (Dictionary<string, List<DataRow>>)
dsShort.Tables[0].AsEnumerable()
.GroupBy(x => new { x[tokens[0]], x[tokens[1]], x[tokens[2]]})
.Where(g => exp.GroupSizeOk((uint)g.Count()))
.OrderBy(g => g.Count())
.ToDictionary(g => g.Key.ToString(), g => g.ToList());
// NOT COMPILING: invalid anonymous type member declarator
break;
}
My questions:
(1) Case 3 is not working, how can I correct it?
(2) Can I make this dynamic? (i.e., a foreach or something similar, so that it works with any number of colunms)
public bool GroupSizeOk(UInt32 size)
{
return (size >= _minGroupSize)
&& (_maxGroupSize > 0 ? size <= _maxGroupSize : true);
}
What you need is an IEqualityComparer for your array that will compare the arrays based on the items in them, rather than by the reference to the array itself. Such a comparer is simple enough to create:
public class SequenceComparer<T> : IEqualityComparer<IEnumerable<T>>
{
private IEqualityComparer<T> itemComparer;
public SequenceComparer(IEqualityComparer<T> itemComparer = null)
{
this.itemComparer = itemComparer ?? EqualityComparer<T>.Default;
}
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<T> obj)
{
unchecked
{
return obj.Aggregate(79,
(hash, next) => hash * 39 + next.GetHashCode());
}
}
}
Now your query will basically work as you intended it to work, with one other key change being transforming your array tokens into an array representing the values from that row that are in the given array:
var dictShort = dsShort.Tables[0].AsEnumerable()
.GroupBy(row => tokens.Select(token => row[token]).ToArray(),
new SequenceComparer<object>())
.Where(g => exp.GroupSizeOk((uint)g.Count()))
.OrderBy(g => g.Count())
.ToDictionary(g => string.Join(", ", g.Key), g => g.ToList());
You had:
.GroupBy(new x => { x[tokens[0]], x[tokens[1]], x[tokens[2]]})
But it should really be :
.GroupBy(x => string.Join(",",new [] { x[tokens[0]], x[tokens[1]], x[tokens[2]]}))
I had made the change in your question before I realized that this is part of your answer. The new goes inside of the lambda.
As for making the code dynamic based on columns, yes you can.
.GroupBy(x => string.Join(",",tokens.Where(w => w.Columns.Contains(w)).Select(s => x[s]).ToArray()))
That should give you a grouping of all the matching columns in the array.
I jsut found the awnswer:
dictShort = dsShort.Tables[0].AsEnumerable()
// This where selects elements if and only if all fields are not null
.Where(x => ListAnd(tokens.Select(t => x[t] != DBNull.Value && IsFilledIn(x[t].ToString())).ToArray()))
.GroupBy(x => String.Join("+", tokens.Select(t => x[t].ToString()).ToArray()))
//.GroupBy(x => x[block]) // TODO
.Where(g => exp.GroupSizeOk((uint)g.Count()))
.OrderBy(g => g.Count())
.ToDictionary(g => g.Key/*.ToString()*/, g => g.ToList());
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);
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)