I have a nested dictionary which looks like this:
Dictionary<string, Dictionary<string, int>> users = new Dictionary<string, Dictionary<string, int>>();
The first string is the name of the user, the second is the contest he is taking part in and the int is his score. One user can take part in multiple contests.
My task is to find the user with the highest score by adding all the points he has. For now I used this code :
foreach (var user in users)
{
bestUsers.Add(user.Key, 0);
foreach (var contest in user.Value)
{
bestUsers[user.Key] += contest.Value;
}
}
I want to know how to do it by using anonymous function looking something like this :
KeyValuePair<string, int> bestUser = users.OrderBy(x => x.Value.Sum());
Instead of a nested dictionary, you can create a class that represents the results of the User:
public class UserGameResults
{
public string Name { get; set; } // the name of the user
public int TotalScore { get => GameResults.Select(x => x.Value).Sum(); } // total score of all games, will be calculated every time the property is accessed
public Dictionary<string,int> GameResults { get; set; } = new Dictionary<string,int>(); // key is the name of the game, value is the score
}
If you use a Dictionary<string,UserGameResults>, you will get your result more easily:
var bestResult = users.OrderByDescending(x => x.Value.TotalScore).FirstOrDefault();
Moreover, a Dictionary<string,UserGameResults>tells you much more about the meaning of the data than the Dictionary<string,Dictionary<string,int>>.
try this
var dict = new Dictionary<string, Dictionary<string, int>> {
{ "name1", new Dictionary<string, int>{ { "A", 2 }, {"B", 3 }}},
{ "name2", new Dictionary<string, int>{ { "C", 4 }, {"D", 5 }}}
};
var scores = dict.Select(d => new { name = d.Key, score = d.Value.Select(x => x.Value).Sum() } )
.ToList().OrderByDescending (d =>d.score );
scores
name2 9
name1 5
You use Dictionary<TKey,TValue> when you need to store values with some unique keys associated to them, and accessing them by that key is convenient for you.
I don't know why you use dictionary here. Your user name must not be unique I think. So if user name is not unique then how can u store all user in a dictionary. I think you should use list instead of Dictionary.
Dictionary<string, Dictionary<string, int>> users = new Dictionary<string, Dictionary<string, int>>()
{
{ "A", new Dictionary<string, int>(){
{"sub1", 10},
{"sub2", 20},
{"sub3", 30}
}
},
{ "B", new Dictionary<string, int>(){
{"sub1", 10},
{"sub2", 40},
{"sub3", 30}
}
}
};
var result = users.OrderBy(x => x.Key).ToDictionary(m => m.Key, m => m.Value.Sum(k => k.Value));
Dictionary is pretty fast, well optimized and offer the performance you need in most cases. But in most of the cases it doesn't really matter, the performance of a chained list of key-value pairs would have also been enough.
For the code refactor to use linq to get the dictionary instead of the 2 foreach loops, you could use something like this:
users.ToDictionary(u => u.Key, u => u.Value.Select(c => c.Value).Sum());
OR I think Sum took an selector lambda
users.ToDictionary(u => u.Key, u => u.Value.Sum(c => c.Value));
Should be valid
Related
public class A {
public string Name {get; set;}
public Dictionary<string, decimal> B {get; set;}
}
How can I order a List< A > using values from B?
Example:
var list = new List<A>(){
new A(){
Name = "data1",
B = new Dictionary<string, decimal>()
{"QTY", 10},
{"PRICE", 20}
},
new A(){
Name = "data2",
B = new Dictionary<string, decimal>()
{"QTY", 100},
{"PRICE", 200}
},
};
**I want to order the list by "QTY" ascending.
Checking the value and also make sure the Key is present in the dictionary
var sortONPrice = list.OrderBy(x => x.B.ContainsKey("QTY") ? x.B["QTY"]:0);
If not then supply a value, I chose zero but it could be any arbitrary value.
assuming you have a var list = new List<A>(), you want
var ordered = list.OrderBy(x => x.B.First(xx => xx.Key == "QTY").Value);
Which of course, does not include any null checks or checks against your dictionary to verify that the key QTY exists. You'll want FirstOrDefault instead of First if you aren't sure if QTY exists on all items, and handle accordingly.
I have tried to merge a list (dynamic) of dictionaries into one single list for a given object, using linq, I saw many questions similars to this one, however in their cases they always consider known number of dictionaries.
Well, this is my sctructure:
I have a query that returns a list objects like this:
public class MyDto
{
public Dictionary<string, string> JsonDictionary { get; set; }
public Guid SomeId1{ get; set; }
public Guid SomeId2{ get; set; }
}
using linq i am doing this:
var q = _uow.TableWithJson.GetAll()
.Include(a=> a.TableId1)
.Include(a=> a.TableAux)
.ThenInclude(b=> b.TableId2)
.Select(r => new MyDto
{
SomeId1 = r.tableId1.Id,
SomeId2 = r.tableId2.Id,
JsonDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(r.JsonContent)
});
At the end, I have a result like this;
{
SomeId1: xxxxx,
SomeId2: yyyyy,
JsonDictionary: [
{key: key1, value: value 1}
{key: key1, value: value 2}
{key: key1, value: value 3}
{key: key1, value: value 4}
],
},
{
SomeId1: xxxxx,
SomeId2: yyyyy,
JsonDictionary: [
{key: key4, value: value 4}
{key: key5, value: value 5}
{key: key6, value: value 6}
],
}, // many other objects with different ids and different dictionaries.
As you can see, in the above snippet SomeId1 and SomeId2 of both objects are the same. So, in this case I would like to group these objects merging the JsonContent field into just one dictionary.
Also tried use .GroupBy but i have not been able to merge this jsonContent as an agregation.
Help!!! Please! =) Hugs
This works if you want to do it this way... However, you did not provide how you wanted to handle conflicts with JsonDictionaries with the same key but different value. In this scenario, I just overrode previously declared values. You'd have to change this if you wanted different behavior.
IEnumerable<MyDto> list = new List<MyDto>(); // This is the stuff you parsed
var results = new Dictionary<Tuple<Guid, Guid>, MyDto>();
foreach (var item in list) {
var key = new Tuple<Guid, Guid>(item.SomeId1, item.SomeId2);
if (results.ContainsKey(key))
foreach (var entry in item.JsonDictionary)
results[key].JsonDictionary[entry.Key] = entry.Value;
else results[key] = item;
}
list = results.Values;
UPDATE:
I wrote it in Linq if you really want it. It's pretty inefficient, but I couldn't think of many other ways to do this easily. If you want efficiency, you should use the above example.
var results = list
.GroupBy(
x => new Tuple<Guid, Guid>(x.SomeId1, x.SomeId2),
(x, y) => new MyDto {
SomeId1 = x.Item1,
SomeId2 = x.Item2,
JsonDictionary = y
.SelectMany(z => z.JsonDictionary)
.ToLookup(z => z.Key, z => z.Value)
.ToDictionary(z => z.Key, z => z.First())
});
So quick idea, not sure if this can be totally done in linq natively.
// lets group all of our id's and I'm not going to use groupby because I'm not a fan.
var itemsById = new Dictionary<string, List<MyDto>>();
foreach(var item in q)
{
if(itemsById.ContainsKey(item.SomeId))
{
itemsById[item.SomeId].Add(item);
}
else
{
itemsById.Add(item.SomeId, new List<MyDto>());
itemsById[item.SomeId].Add(item);
}
}
so now we have dictionary of all of items by their ID.
var finalizedDtos = new List<MyDto>();
foreach(var entry in items)
{
var finalizedDto = new MyDto{ someId = entry.Key };
foreach(var innerDictionary in entry.value.JsonDictionary)
{
var finalizedDto = new MyDto {SomeId = entry.Key};
var allKeyValuePairs = entry.Value.SelectMany(c => c.JsonDictionary);
finalizedDto.JsonDictionary = allKeyValuePairs.ToDictionary(key => key.Key, value => value.Value);
finalizedDtos.Add(finalizedDto);
}
}
Not a whole lot of linq, but really for the nested structure I couldn't come up with a better plan
I have 20 dictionaries of <Datetime,double> that are mostly for the same range of dates (e.g. feb 24-2012 through june 4 2012). Some dictionaries have extra days, and some are missing days. I want an array of all the unique dates being used.
Currently I'm iterating through all the keys and adding to a hashset to get a unique set, then converting the hashset to an array. Is there a more efficient way?
For the record I also considered iterating through and using the containsKey function of the dictionary and add to a list, or LINQ. My existing process seems to do the trick.
The code you described is the most efficient you can get.
You can do it with less code (and similar efficency) with LINQ:
dicts.SelectMany(d => d.Keys).Distinct().ToArray();
you can pull all the dictionaries into list that allow "duplicates in keys" and then use the Distinct function:
Dictionary<DateTime, double> dic = new Dictionary<DateTime, double>()
{
{DateTime.Now, 111}
};
Dictionary<DateTime, double> dic2 = new Dictionary<DateTime, double>()
{
{DateTime.Now, 111}
};
var list = dic.ToList();
list.AddRange(dic2.ToList());
var final = list.Distinct().ToDictionary(x => x.Key, x => x.Value);
I found this post while looking for a solution to a slightly different problem but used the accepted answer as the basis for my solution, so I thought someone with the same problem might come down this path too.
I was looking for a way to find a single property in a group of objects that was unique across the property set for each object. I have the property names in dictionaries and I wanted a list of keys that only appear in one dictionary.
Here's my solution, which you should just be able to paste into linqpad to see it working.
void Main()
{
var d = new Dictionary<string, Dictionary<string, string>>
{
{
"First",
new Dictionary<string, string>
{
{"A", "ash"},
{"B", "brett"},
{"R", "ripley"},
{"J", "jones"},
{"D", "dallas"}
}
},
{
"Second",
new Dictionary<string, string>
{
{"A", "ash"},
{"B", "brett"},
{"R", "ripley"},
{"D", "dallas"},
{"K", "kane"}
}
},
{
"Third",
new Dictionary<string, string>
{
{"A", "ash"},
{"B", "brett"},
{"R", "ripley"},
{"D", "dallas"},
{"V", "vasquez"}
}
},
{
"Fourth",
new Dictionary<string, string>
{
{"A", "ash"},
{"B", "brett"},
{"R", "ripley"},
{"D", "dallas"},
{"H", "hicks"}
}
}
};
var u = d.Values.SelectMany(x => x.Keys).Distinct().Where(y => d.Values.SelectMany(z => z.Keys).Count(a => a == y) == 1).ToArray();
foreach (var f in u)
{
Console.WriteLine("{0} => {1}", f, d.Keys.Single(s => ((Dictionary<string, string>)d[s]).ContainsKey(f)));
}
}
I've a Dictionary, which I want to convert to another Dictionary, using the rule implied by below sample:
Inputs:
Dictionary<string, string> inputs = new Dictionary<string, string>(3)
{
{ "A", "First" },
{ "Z", "Third" },
{ "J", "Second" }
};
Output:
Dictionary<int, string> output = new Dictionary<string, string>(3)
{
{ 0, "First" },
{ 1, "Second" },
{ 2, "Third" }
};
Can I do it using lambda syntax & no intermediary objects?
Thanks.
The order of enumeration of a dictionary is indeterminate (i.e. items don't have an index), so I'm not sure that this is possible. How would the integer values be derived?
EDIT:
I get it now:
inputs
.OrderBy(input => input.Key)
.Select((input, index) => new {index, input.Value})
.ToDictionary(x => x.index, x => x.Value)
If you can define what the ordering should be, then I would do it like this (I chose to order by key):
Dictionary<string, string> inputs = new Dictionary<string, string>(3)
{
{ "A", "First" },
{ "Z", "Third" },
{ "J", "Second" }
};
var outputs = inputs.OrderBy(i=>i.Key).Select(i=>i.Value).ToArray();
// output
// String [] (3 items):
First
Second
Third
This gives you an array with the indices that you asked for (e.g. output[0]).
If you really want dictionary entries back, you can get an ienumerable of them like this (you can't just return a dictionary because they're unordered):
var outputs = inputs.OrderBy(i=>i.Key).Select(
(entry, index) => new KeyValuePair<int, string>(index, entry.Value));
Throw a .ToArray() on there if you need to.
If you really want a dictionary back, try this:
var outputs = inputs.OrderBy(i=>i.Key)
.Select((entry, i) => new { entry.Value, i })
.ToDictionary(pair=>pair.i, pair=>pair.Value).Dump();
Just keep in mind that dictionaries are not inherently ordered so if you enumerate over it, you should add a .OrderBy again.
How to convert
Dictioanry<String,List<String>> into Dictionary<String,String>
i'm having a dictionary like
Dictioanry<String,List<String>>dictOne=new Dictionary<String,List<String>>();
and which containg
Key(String) Value(List<String>)
A a1,a2
B b1,b2
C c1
i need to convert the "dictOne" into
Dictionary<String,String> dictReverse=new Dictionary<String,String>()
So the result will be like
Key(String) Value(String)
a1 A
a2 A
b1 B
b2 B
c1 C
is there any way to do this using LINQ
Thanks in advance
Update: As others have noted, in order for a dictionary to be truly "reversible" in this way, the values in your List<string> objects need to all be unique; otherwise, you cannot create a Dictionary<string, string> with an entry for every value in your source dictionary, as there would be duplicate keys.
Example:
var dictOne = new Dictionary<string, List<string>>
{
{ "A", new List<string> { "a1", "a2" } },
{ "B", new List<string> { "b1", "b2" } },
{ "C", new List<string> { "c1", "a2" } } // duplicate!
};
You have (at least) two options for dealing with this.
Option 1: Throw on duplicates
You may want to ensure that every element in every List<string> is, in fact, unique. In this case, a simple SelectMany with a ToDictionary will accomplish what you need; the ToDictionary call will throw an ArgumentException on encountering a duplicate value:
var dictTwo = dictOne
.SelectMany(kvp => kvp.Value.Select(s => new { Key = s, Value = kvp.Key }))
.ToDictionary(x => x.Key, x => x.Value);
The most generic way (that comes to mind) to abstract this functionality into its own method would be to implement an extension method that does this for any IDictionary<T, TEnumerable> implementation where TEnumerable implements IEnumerable<TValue>:
// Code uglified to fit within horizonal scroll area
public static Dictionary<T2, T1> ReverseDictionary<T1, T2, TEnumerable>(
this IDictionary<T1, TEnumerable> source) where TEnumerable : IEnumerable<T2>
{
return source
.SelectMany(e => e.Value.Select(s => new { Key = s, Value = e.Key }))
.ToDictionary(x => x.Key, x => x.Value);
}
The ugly proliferation of generic type parameters in the above method is to allow for types other than strictly Dictionary<T, List<T>>: it could accept a Dictionary<int, string[]>, for example, or a SortedList<string, Queue<DateTime>> -- just a couple of arbitrary examples to demonstrate its flexibility.
(A test program illustrating this method is at the bottom of this answer.)
Option 2: Skip duplicates
If duplicate elements in your List<string> values is a realistic scenario that you want to be able to handle without throwing an exception, I suggest you take a look at Gabe's excellent answer for an approach that uses GroupBy (actually, Gabe also provides a flexible approach that can cover either of these two cases based on a selector function; however, if you definitely want to throw on a duplicate, I'd still suggest the above approach, as it should be somewhat cheaper than using GroupBy).
Example program
Here's a little test program illustrating Option 1 above on a Dictionary<string, List<string>> with no duplicate elements in its List<string> values:
var dictOne = new Dictionary<string, List<string>>
{
{ "A", new List<string> { "a1", "a2" } },
{ "B", new List<string> { "b1", "b2" } },
{ "C", new List<string> { "c1" } }
};
// Using ReverseDictionary implementation described above:
var dictTwo = dictOne.ReverseDictionary<string, string, List<string>>();
foreach (var entry in dictTwo)
{
Console.WriteLine("{0}: {1}", entry.Key, entry.Value);
}
Output:
a1: A
a2: A
b1: B
b2: B
c1: C
// Associates each key with each of its values. Produces a sequence like:
// {A, a1}, {A, a2}, {B, b1}, {B, b2}, {C, c1}
var kvps = from kvp in dictOne
from value in kvp.Value
select new { Key = kvp.Key, Value = value };
// Turns the sequence into a dictionary, with the old 'Value' as the new 'Key'
var dictReverse = kvps.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
Of course, each key in the original dictionary must be associated with a unique set of values, and no key must be associated with values that are also associated with other keys.
Also bear in mind that Dictionary<K, V> does not define any sort of enumeration order. You can use the Enumerable.OrderBy method to enumerate the resulting dictionary in the appropriate order.
In the event that you would end up with duplicate keys in your result dictionary, you would have to pick a single one of those keys. Here's an implementation that just picks the first one it sees (using First):
var dictReverse = (from kvp in dictOne
from value in kvp.Value
group kvp.Key by value)
.ToDictionary(grp => grp.Key, grp => grp.First());
Given this input dictionary:
var dictOne = new Dictionary<string, IEnumerable<string>> {
{ "C", new List<string> { "c1", "a2" } },
{ "B", new List<string> { "b1", "b2" } },
{ "A", new List<string> { "a1", "a2" } } };
The result would be:
c1: C
a2: C
b1: B
b2: B
a1: A
As Dan points out, you may want different behavior in the case of duplicate keys. You can create this function:
public static Dictionary<V, K> Transpose<K, V>(
this Dictionary<K, IEnumerable<V>> dictOne,
Func<IEnumerable<K>, K> selector)
{
return (from kvp in dictOne
from V value in kvp.Value
group kvp.Key by value)
.ToDictionary(grp => grp.Key, grp => selector(grp));
}
Then you could call it like dictOne.Transpose(Enumerable.First) to get the above behavior, dictOne.Transpose(Enumerable.Single) to get an exception when there's a duplicate key (the behavior of other posts), dictOne.Transpose(Enumerable.Min) to pick the first one lexicographically, or pass in your own function do whatever you need.