LINQ ToDictionary how to get item index? - c#

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.

Related

C# Sort a Dictionary by The Numerical value of string (Key)

Interesting question here. I'm trying to sort a Dictionary<string, string> by numeric value. It works in case of List<string> but not Dictionary, what am I doing wrong here?
Dictionary<string, string> s = new Dictionary<string, string>() {
{ "a", "a" },
{ "100", "1" },
{ "2", "2" },
{ "10", "10" },
};
List<string> g = new List<string> {
"1", "10", "a", "1000"
};
var v = 0;
var problem = s.OrderBy(x => int.TryParse(x.Key, out v));
// outputs original order
var works = from c in g
orderby int.TryParse(c,out v)
select c;
// outputs sorted order a, 1, 10, 1000
You have two distinct, massive problems, that you need to understand.
You are not sorting by v. You are sorting by whatever is returned by the comparer passed into the OrderBy call, which in your sample is a bool.
So, you are doing this:
var g = new List<string> { "1", "10", "a", "1000" };
var works = from c in g
orderby int.TryParse(c,out v)
select c;
// It doesn't work, because int.TryParse outputs [true, true, false, true]
// which is used as a sorting key, which puts the 'a' as the first element,
// because false is sorted before true.
// try with this input:
var g = new List<string> { "10", "1", "a", "1000" };
// and you'll see that the output is ["a", "10", "1", "1000"]
So, your sorting function should be int.TryParse(c,out v) ? v : -1.
The -1 could be something different if you need a sorting also on the invalid keys, but that can be addressed by a ThenBy after the OrderBy.
BUT, you still have a major bug:
You are always updating the single variable v. If the value is calculated and stored in a shared variable it could easily lead to problems with late evaluation, so you should remove var v from the external scope and use
// the addition of 'var' in the out declaration enables the usage of a separate, new v for each item in the source,
// thus removing the possibility of collisions.
s.OrderBy(x => int.TryParse(x.Key, out var v) ? v : -1);
// if you need to put the alphanumeric keys last, and maybe sorting
// them alphabetically, you can also use something like:
s.OrderBy(x => int.TryParse(x.Key, out var v) ? v : int.MaxValue)
.ThenBy(x => x) // sorts by the string itself
;
You can try something like this:
var problem = s
.OrderBy(pair => int.TryParse(pair.Key, out var parsed)
? parsed // Take Key value as it is
: long.MinValue); // Treat pair as the topmost
Here we try to parse Key from the key-value pair and in case we fail, we put the pair on the top (I've used long.MinValue in order to mix with possible int.MinValue key). You may want to add .ThenBy:
var problem = s
.OrderBy(pair => int.TryParse(pair.Key, out var parsed)
? parsed // Take Key value as it is
: long.MinValue) // Treat pair as the topmost
.ThenBy(pair => pair.Key);
if you want to sort non-number keys as well as numbers, e.g.
Dictionary<string, string> demo = new() {
{ "a", "a" },
{ "100", "1" },
{ "2", "2" },
{ "b", "a" },
{ "10", "10" },
{ "c", "a" },
};
var result = demo
.OrderBy(pair => int.TryParse(pair.Key, out var parsed)
? parsed
: long.MinValue)
.ThenBy(pair => pair.Key);
Console.Write($"{string.Join(Environment.NewLine, result)}");
Output:
[a, a]
[b, a]
[c, a]
[2, 2]
[10, 10]
[100, 1]
Edit: if you want to put numerics first, all you should do is to change long.MinValue to long.MaxValue:
var result = demo
.OrderBy(pair => int.TryParse(pair.Key, out var parsed)
? parsed
: long.MaxValue)
.ThenBy(pair => pair.Key);

How can I replicate this code by using anonymous function (lambda)?

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

Export dictionary to a JSON, sorted by keys

I have a dictionary
Dictionary<string, object>
(the values are either string, int or double).
I export the dictionary to a JSON file:
JsonConvert.SerializeObject(myDict);
I want the saved JSON to be sorted by keys.
If you use a SortedDictionary<TKey, TValue>, it'll be serialized in order of the keys:
var dict = new SortedDictionary<string, int>
{
{ "Z", 3 },
{ "B", 2 },
{ "A", 1 },
};
var json = JsonConvert.SerializeObject(dict);
Console.WriteLine(json);
Output:
{"A":1,"B":2,"Z":3}
You could use SortedDictionary
https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.sorteddictionary-2?view=netframework-4.7.2
It sorts your entries by key so you don't need to perform any sorting on your own.
Just use temporary SortedDictionary object:
var unsortedDic = new Dictionary<string, int>
{
{"Z", 3},
{"B", 2},
{"A", 1},
};
var sortedJson = JsonConvert.SerializeObject(new SortedDictionary<string, int>(unsortedDic));
Output:
{"A":1,"B":2,"Z":3}
You can sort your dictionary like this:
JsonConvert.SerializeObject(myDict.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value));

need get unique set of keys from Multiple dictionaries<Datetime,double>

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

Reversing Dictionary using LINQ in C#

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.

Categories