I have a dictionary of customers and associated data (e.g. MoneySpent) and would like to return the sorted list of customers only.
The only solution I figured out so far is this:
CustomerData<Customer, int> //the value here is the money spent
List<KeyValuePair<Customer, int>> sortedListByValue = CustomerData.OrderByDescending(s => s.Value).ToList();
Then just going throug the list and obtaining keys. However, I am sure there is an easier way of doing and would be glad for advice.
You can select the Key which will give you the customers.
var sortedListByValue = CustomerData.OrderByDescending(s => s.Value)
.Select(x => x.Key).ToList();
Related
I have a legacy code which converts lists to dictionary by doing some manipulation as shown below
var items = await processTask;
var itemDict = items.ToDictionary(dto => dto.ClientId, dto => mapper.ConvertTo(dto, hj));
But recently we started to see this issue which looks like we are getting duplicate keys
An item with the same key has already been added
What's the best way to fix this so that if duplicate keys comes it should not throw exception but we can log it. Can we do this in linq or it has to be done in for loop?
Unfortunately, you can't eliminate dups while in ToDictionary. You have to do something before ToDictionary to eliminate it, like call Distinct or similar. But may be better to have an explicit loop, where you get opportunity to do something with a dupe
var dict = new Dictionary<int, string>(); // whatever mapper converts to
foreach(var dto in items)
{
if (dict.ContainsKey(dto.ClientId))
{
// log duplicate here or do something
continue;
}
dict.Add(dto.ClientId, mapper.ConvertTo(dto, hj));
}
You can do it with LINQ using GroupBy:
var itemDict = items
.GroupBy(dto => dto.ClientId)
.ToDictionary(gr => gr.Key, gr => mapper.ConvertTo(gr.First(), hj))
Also logging duplicates will make this code less elegant.
I am sure it has been answered somewhere before but for the love of god I cant find it.
I want to get a specific Value for a Key from a KeyValuePair List per LINQ one-liner.
My List: List<KeyValuePair<int, int>> LeagueKVPList
I think it goes something like this:
int x = LeagueKVPList.Where(v => v.Key.(int y)).Value
But that obviously does not work.
Thanks for any help.
You should use Select for that
var values = LeagueKVPList.Select(kvp => kvp.Value);
It returns you all values.
To get a single value you can use FirstOrDefault
var x = LeagueKVPList.FirstOrDefault(kvp => kvp.Key == y).Value;
I create a multiple keys dictionary as (registrantsFields is enumerable type)
var registrantsRepository = registrantsFields.ToDictionary(c => Tuple.Create(c.RegistrantID, c.FieldID, c.DataID));
I use ContainsKey to search the dictionary as
if (registrantsRepository.ContainsKey(Tuple.Create(registrantId, fieldId, dataId)))
So far it works fine.
But I want to search the dictionary with only 2 keys, i.e. what dictionary contains for certain registrantId and fieldId, but with any dataId. In other word, I like to find all items like
var entries = registrantsRepository(Tuple.Create(registrantId, fieldId, *))
How should it be done (perhaps in Linq)? Thanks.
I'd just create a separate Lookup.
var registrantsByIdAndField = registrantsFields
.ToLookup(r => Tuple.Create(c.RegistrantID, c.FieldID));
Then you still get fast lookups with this:
var entries = registrantsByIdAndField[Tuple.Create(registrantId, fieldId)];
There's no wildcard search like that, but there is a way to ignore those fields that you're not interested in when you're doing your search.
Iterate through the collection of Keys, referencing the properties of your Tuple that you're interested in matching on. You can do this using LINQ's Any method.
if (registrantsRepository.Keys.Any(x => x.Item1 == registrantId && x.Item2 == fieldId)
{
}
All good answers here. Is ToLookup an option for you?
https://msdn.microsoft.com/en-us/library/bb549073(v=vs.100).aspx
Edit: just realized #StriplingWarrior beat me to this!
Having IEnumerable<Order> orders, how to get a Dictionary<string, IEnumerable<Order>> using Linq, where the key is Order.CustomerName mapped to a IEnumerable of customer's orders.
orders.ToDictionary(order => order.CustomerName) is not going to work right away, since there could be multiple orders that could have the same CustomerName.
Solution: orders.ToLookup(order => order.CustomerName);
The ILookup interface is designed for this purpose, and represents a dictionary-like structure that contains many values per key. It has similar performance characteristics to those of a dictionary (i.e. it's a hashtable type structure)
You can create an ILookup using the .ToLookup extension method as follows:
ILookup<string, Order> ordersLookup = orders.ToLookup(o => o.CustomerName)
then:
IEnumerable<Order> someCustomersOrders = ordersLookup[someCustomerName];
Just an alternative to #spender's answer, if you really want a type Dictionary<string, IEnumerable<Order>>, you could use:
Dictionary<string, IEnumerable<Order>> dictionary = orders
.GroupBy(order => order.CustomerName)
.ToDictionary(groupedOrders => groupedOrders.Key,
groupedOrders => (IEnumerable<Order>)groupedOrders);
I'm sure there's a nicer way, but that'll do it too.
Or you could probably simply use
orders.ToLookup(o => o.CustomerName).ToDictionary(g => g.Key)
But as Spender's answer indicates, maybe you don't need the last method, ToDictionary.
So I a collection of dictionary items in a list:
List<Dictionary<string, string>> inputData = new List<Dictionary<string, string>>(inputs);
List<Dictionary<string, string>> itemStack = new List<Dictionary<string, string>>();
Now what I want to do is for each inputData dictionary item I want to check if itemStack has the same value (Dictionary Item) already.
I was thinking it would be like?
foreach (var item in inputData)
{
if(!itemStack.Contains(item){ itemStack.Add(item)}
else{ //Duplicate found}
}
It doesn't really check the items values inside? It just assumes that it doesn't have it...
All i want is if itemStack contains and item that is already in the stack don't include it.
I know I'm missing something obvious.
Thanks,
Dictionary is reference type, so it doesn't check the "deep" value like you expected.
You will have to write your own "Contains" method, either as totally separate method or extension of the Dictionary itself then use it instead, for example:
if(!MyContains(itemStack, item)){ itemStack.Add(item)}
True that a HashSet would be better, but if you want to do it here, try this (assuming you are filtering duplicate keys only):
foreach (var item in inputData.Keys)
{
if (itemStack.Where(x => x.Key == item.Key).Count() > 0)
// There was a duplicate
}
Or, if you only care when the data is coming out you can call:
itemStack.Distinct()
I think, your way is right. On my mind, HashSet is good, but when you add a new element, it performs the same test on the contents of the same items.
Regards.
Based on your initial problem statement, you might do something like this:
var aggregateKnownKeys = itemStack.SelectMany(d => d.Keys);
itemStack.AddRange(
inputData.Select(d=> d.Where(p => !aggregateKnownKeys.Contains(p.Key))
.ToDictionary(p => p.Key, p => p.Value)));
If you only need to combine two dictionaries then you could do this to skip keys that exist in itemStack:
var inputData = new Dictionary<string, string>();
var itemStack = new Dictionary<string, string>();
var oldStack = itemStack;
itemStack = new[] { inputData.SkipWhile(d => oldStack.Keys.Contains(d.Key)), itemStack }
.SelectMany(d => d)
.ToDictionary(d => d.Key, d => d.Value);
Okay so this isn't quite a full answer but it's what I did.
So I have a List of items and instead of doing a full compare to whats in an List(Hence the other considered) I just did a single item check:
if(!String.IsNullOrEmpty(item["itemId"]))
{
alert.DaleksApproaching(item["itemId"]);
}
So when it does see it has a value it just does another event to get rid of it.
The idea of using LINQ and the method approaches about(Contains and Distinct)I like. I have yet to try that, but I plan on doing that. For this it doesn't use LINQ :(
Thanks everyone!