How to combine duplicate values in dictionary into one value - c#

I have dictionary with duplicate values. How to join this duplicate values into one value
Example:
Accord - first
Accord.s - first
I want to see something like:
Accord, Accord.s - first

If I've understood you right, you have a dictionary
Dictionary<string, string> source = new Dictionary<string, string>() {
{"Accord", "first"},
{"Accord.s", "first"},
{"Gamma", "second"},
};
and you want to group by Value, which you can do with a help of Linq:
using System.Linq;
...
// If you want to create a dictionary:
Dictionary<string, string> result = source
.GroupBy(pair => pair.Value)
.ToDictionary(
chunk => string.Join(", ", chunk.Select(pair => pair.Key)),
chunk => chunk.Key);
string report = string.Join(Environment.NewLine, result
.Select(pair => $"{pair.Key} : {pair.Value}"));
Console.Write(report);
Outcome:
Accord, Accord.s : first
Gamma : second
In case you want just a query (not dictionary)
var result = source
.GroupBy(pair => pair.Value)
.Select(chunk => new {
Key = string.Join(", ", chunk.Select(pair => pair.Key)),
Value = chunk.Key});
// and then
string report = string.Join(Environment.NewLine, result
.Select(pair => $"{pair.Key} : {pair.Value}"));

Related

Order Dictionary by the result of other dictionary

Looking for someone to help me with C#, LINQ.
I have a Dictionary<int,int> that I am ordering like so: .OrderBy(_ => _.Value).ThenBy(_ => ThisMethodReturnsAnotherIReadOnlyDict<int,int>()).ThenByDescending(_ => _.Key).
What I want is to order the first dictionary by its value and then if there are still equal values I want that tie to be broke by the ThisMethodReturnsAnotherIReadOnlyDict<int,int>(). This first key/value of this ThisMethodReturnsAnotherIReadOnlyDict to break the tie and be on top. And finally, if everything fails, then order by it's key descending.
Some data for this like:
(First Dictionary)
[1,400]
[2,550]
[3,200]
[4,200]
(Second dictionary)
[3,50]
[4,140]
[2,600]
[1,700]
For this scenario I want my ordering to return: [3,50]
Can anyone help please?
Thanks!
It looks like you're looking for something like this:
var firstDict = new Dictionary<int, int>() {
{1,400},
{2,550},
{3,200},
{4,200}
};
var secondDict = new Dictionary<int, int>() {
{3,50},
{4,140},
{2,600},
{1,700}
};
var result = (from kvp in firstDict
join tieBreaker in secondDict on kvp.Key equals tieBreaker.Key
select new { kvp.Key, V1 = kvp.Value, V2 = tieBreaker.Value })
.OrderBy(x => x.V1)
.ThenBy(x => x.V2)
.ThenByDescending(x => x.Key)
.First();
This will join the first and second dictionaries together by its keys and will, respectively, order by the value of the first dictionary, the value of the second dictionary and then descending by the key itself.
How about:
var ans = firstDict
.OrderBy(kv => kv.Value)
.ThenBy(kv => ThisMethodReturnsAnotherIReadOnlyDict<int,int>().TryGetValue(kv.Key, out var val2) ? val2 : kv.Key)
.ToList();
Unless ThisMethodReturnsAnotherIReadOnlyDict<int,int> may change, you may want to cache the return value in a variable before sorting.

LINQ using .ToDictionary without .Select

I have the following dictionary:
Dictionary<string, string> clauses = new Dictionary<string, string>();
where the clauses are like this:
"A|B" - "some text"
"A|D|E" - "some text"
"G" - "some text"
"E|A" - "some text"
...
and I want to populate the dictionary below:
Dictionary<string, int> columnsBitMap = new Dictionary<string, int>();
where the string values are the unique letters of the first dictionary strings and int values are calculated by math formula.
I have the following which is working perfectly:
columnsBitMap = String.Join("|", clauses.Select(clause => clause).Select(clause => clause.Key)).Split('|')
.Distinct().OrderBy(column => column)
-- can I remove the next Select ?
.Select((column, index) => new KeyValuePair<string, int>(column, index))
.ToDictionary(column => column.Key, column => Convert.ToInt32(Math.Pow(2, column.Value)));
but I am wondering if this could be simplified removing the .Select part?
The output should be like this:
A 1
B 2
D 4
E 8
G 16
This bit is completely superfluous:
.Select(clause => clause)
Just remove it and the rest should work fine.
I dont see much reason to get rid of the part
.Select((column, index) => new KeyValuePair<string, int>(column, index))
But if you're against using a KeyValuePair<TKey,TValue> you could just make it an anonymous object
.Select((column, index) => new{ Key = column, Value = index })
But there's not a great amount of difference.
I approached your requirement in a slightly different way:
var result = clauses.SelectMany(clause => clause.Key.Split('|'))
.Distinct().OrderBy(column => column)
.Select((column, index) => new {Key=column,Value=index})
.ToDictionary(column => column.Key, column => Convert.ToInt32(Math.Pow(2, column.Value)));
Working example with your test case: http://rextester.com/PWC41147

Group Dictionary by splitting the Key and create new Dictionary

I have a Dictionary like this:
Dictionary<string, object> properties = new Dictionary<string, object>()
{
{"aa:bb", MyObject1},
{"aa:cc", MyObject2},
{"dd:xx", MyObject3},
{"dd:yy", MyObject4}
};
The key of the dictionary is a string with ':' as delimiter. Now I want do create from that Dictionary a new one:
Dictionary<string, object> ddProperties = new Dictionary<string, object>()
{
{"xx", MyObject3},
{"yy", MyObject4}
};
I'm looking for an elegant way to create the new Dictionary by splitting the key of the original Dictionary. Is that possible with LINQ?
Try:
var ddProperties = properties.ToDictionary
(kvp => kvp.Key.Split(':')[1], kvp => kvp.Value);
If you only need the ones beginning with dd (as can be seen in your sample output), I would do:
var filteredPairs = from kvp in properties
let split = kvp.Key.Split(':')
where split[0] == "dd"
select new { Key = split[1], kvp.Value };
var ddProperties = filteredPairs.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Do note that there is no protection against duplicates here. If you're expecting duplicate keys after transformation, how would you like to handle them?
note that since each entry is multiple results, use .SelectMany().
As mentioned, ToDictionary builds dictionaries nicely
properties
.SelectMany(kvp => kvp.Key.Split(':')
// Select each item in the split
// to gain access to the original Key Value Pair parameter
.Select(key => new { Key = key, Value = kvp.Value }))
.ToDictionary(
a => a.Key,
a => a.Value);

How can I retrieve duplicate key value pairs?

public static IEnumerable<KeyValuePair<string, string>> GetGroupKeyValuePairs(string category)
{
var list = new List<KeyValuePair<string, string>>();
using (DataConnection connection = new DataConnection())
{
List<KeyValuePair<string,string>> settings = connection.Get<Settings>()
.Where(a => a.Category == category )
.Select(pair => new KeyValuePair<string,string>(pair.TheName, pair.TheValue))
.ToList();
list = settings;
}
return list;
}
The exception is:
InvalidOperationException:
Key 'Garanti.Oda' appears more than one time
How can I collect duplicate keys?
The method that you show isn't going to have a problem with multiple pairs with the same key. I assume that afterward, you're doing something like creating a dictionary of these pairs, and that's where you have a problem. E.g.
var pairs = GetGroupKeyValuePairs("some category");
var dict = new Dictionary<string, string>();
foreach (var pair in pairs)
dict.Add(pair.Key, pair.Value); // exception when it hits a duplicate
Instead, you need to use the pairs in a way that's friendly to duplicates, e.g. ToLookup.
var pairs = GetGroupKeyValuePairs("some category");
var lookup = pairs.ToLookup(x => x.Key, x => x.Value);
Then, for example if the list had "a", "b" and "a", "c", then lookup["a"] gives you "b" and "c".
Assuming you want to find duplicates by Key only (e.g. so that you can build a dictionary), you could GroupBy the prospective key and find all instances of more than one:
var dupeSettings = connection.Get<Settings>()
.Where(a => a.Category == category)
.GroupBy(a => a.TheName)
.Where(grp => grp.Count() > 1)
.Select(dupe => dupe.Key)
.ToList();
Or, if you want duplicates of both key and value, project and group by an anonymous class:
var dupeSettings = connection.Get<Settings>()
.Where(a => a.Category == category)
.GroupBy(a => new {a.TheName, a.TheValue})
.Where(grp => grp.Count() > 1)
.Select(dupe => dupe.Key) // Key.TheName, Key.TheValue
.ToList();

Convert String list to a dictionary

I Have a string list like this ["saman=1", "kaman=2"]
How may I convert this to a dictionary like {Saman:1 , kaman:2}
strList.Select(k,v =>new {k,v} , k=> k.split('=')[0], val => v.split('=')[1]);
This should work:
strList.ToDictionary(x => x.Split('=')[0], x => x.Split('=')[1])
If you want Dictionary<string, int> you can parse the Value to integer:
strList.ToDictionary(x => x.Split('=')[0], x => int.Parse(x.Split('=')[1]))
You should split by ", " first, and then split each item by = to get key/value pairs.
Additional Trim call will get rid of [" at the beginning and "] at the end of your input string.
var input = #"[""saman=1"", ""kaman=2""]";
var dict = input.Trim('[', '"', ']')
.Split(new [] {#""", """}, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split('='))
.ToDictionary(x => x[0], x => x[1]);
Very, very simply with LINQ:
IDictionary<string, string> dictionary =
list.ToDictionary(pair => pair.Key, pair => pair.Value);
Note that this will fail if there are any duplicate keys - I assume that's okay?

Categories

Resources