Рow to get 2 to 4 of the object element - c#

I had error from line
Dictionary<int, string> viewBusinesAndCountLeft = grptemp.Take(4).ToDictionary(x => x.Count, x => x.BusinessCategoryName);
error: "The element with the same key has already been added."
How to do this?
var grptemp = (from adsBusines in m_adsRepository.SaleBusinesAds
group adsBusines by adsBusines.BusinessCategory.Name
into grp
select new
{
BusinessCategoryName = grp.Key,
Count = grp.Select(x => x.BusinessCategory.ChildItems.Count()).Distinct().Count()
}).Take(8);
Dictionary<int, string> viewBusinesAndCountLeft = grptemp.Take(4).ToDictionary(x => x.Count, x => x.BusinessCategoryName);
Dictionary<int, string> viewBusinesAndCountRigth = grptemp.Skip(4).Take(4).ToDictionary(x => x.Count, x => x.BusinessCategoryName);

You are using the count as the key for the dictionary which means you will throw that exception whenever you happen to find two categories with the same count.
If I understand what you're trying to do correctly, you should have the 'business category' as the key to the dictionary and the count as the value.
E.g.
Dictionary<string, int> viewBusinesAndCountLeft = grptemp.Take(4).ToDictionary(x => x.BusinessCategoryName, x => x.Count);

Declaring a Dictionary<T,U> means a dictionary with key of type T (in this case, int) - and the key in a dictionary must be unique.
You probably want the key to be the string, and the value to be the count. Try switching your arguments:
Dictionary<string, int> viewBusinesAndCountLeft =
grptemp.Take(4).ToDictionary(x => x.BusinessCategoryName, x => x.Count);

Related

Populate new dictionary from old dictionary

I have a Dictionary<string, int> where the string is a randomized collection of characters and the int is the ASCII sum of that string.
e.g.
["aaaaaaaaab", 971],
["aaaaaaaaba", 971],
["aaaaaaabaa", 971],
["aaaaaabaaa", 971]
I would like to make a new dictionary from the original where the new key is the value from the original, and the new value is the List<string> which would contain all the strings with the key as the ASCII sum.
e.g.
[971, List<string>{ "aaaaaaaaab", "aaaaaaaaba", "aaaaaaabaa", "aaaaaabaaa"}]
How can I achieve this? I cannot wrap my head around the required steps.
Use could GroupBy and ToDictionary
The premise is :
group by the old Value
project to a new dictionary given the values of the GroupBy
which will be the grouped list of KeyValuePair from the original dictionary, that in-turn has the key selected out of it (.Select(y => y.Key)
Example
var newDict = old.GroupBy(x => x.Value)
.ToDictionary(x => x.Key, x => x.Select(y => y.Key)
.ToList());
Additional Resources
Enumerable.GroupBy Method
Groups the elements of a sequence.
Enumerable.ToDictionary Method
Creates a Dictionary<TKey,TValue> from an IEnumerable<T>.
Since values are not unique, you need to group by Value before converting to dictionary:
var inverse = original
.GroupBy(p => p.Value)
.ToDictionary(g => g.Key, g => g.Select(p => p.Key).ToList());
If you wanted to do this without Linq, you could do the following:
foreach(KeyValuePair<string, int> entry in dict) {
if(!dict2.ContainsKey(entry.Value)) {
dict2[entry.Value] = new List<string>();
}
dict2[entry.Value].Add(entry.Key);
}
Assuming you have dict defined as Dictionary<string, int> dict and dict2 defined as Dictionary<int, List<string>> dict2
Here is a complete example for anyone that wants to "wrap their head around" how to do this, without LINQ.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Dictionary<string,int> origDict = new Dictionary<string,int>{{"tttt",1},{"fttt",1},{"fftt",2}};
var vals = new int[origDict.Count];
origDict.Values.CopyTo(vals,0);
var keys = new string[origDict.Count];
origDict.Keys.CopyTo(keys,0);
Dictionary<int,List<string>> newDict = new Dictionary<int,List<string>>();
for(int i = 0; i < vals.Length; i++){
int val = vals[i];
if(newDict.ContainsKey(val)){
newDict[val].Add(keys[i]);
}else{
newDict[val] = new List<string>();
newDict[val].Add(keys[i]);
}
}
foreach(var key in newDict.Keys){
Console.WriteLine(key);
foreach(var val in newDict[key]){
Console.WriteLine(val);
}
}
}
}
Output:
1
tttt
fttt
2
fftt

C# LINQ Merge Dictionaries by comparing keys

I have 2 dictionaries as below
Dictionary<string, string> first= new Dictionary<string, string>();
Dictionary<string, string> second = new Dictionary<string, string>();
first.Add("NAME", "NAME");
first.Add("TYPE", "TYPE");
first.Add("REF","REF");
first.Add("NUMBER", "NUMBER");
first.Add("DATE", "DATE");
first.Add("SOURCE", "SOURCE");
second.Add("TYPE", "T1");
second.Add("REF","A1234");
second.Add("NUMBER", "B456");
second.Add("DATE", "D123455");
second.Add("NAME", "NAME");
second.Add("SOURCE", "SOURCE");
How to achieve a resulting dictionary like below
("NAME", "NAME");
("TYPE", "T1");
("REF","A1234");
("NUMBER", "B456");
("DATE", "D123455");
("SOURCE", "SOURCE");
Ideally first and second merged together with the values from second merged onto first and the keys should retain in the order of first dictionary.
Could anyone suggest the best approach to achieve this, Thanks.
var result = first.Union(second)
.GroupBy(x => x.Key)
.Select(x => new { Key = x.Key, Value = x.Last().Value })
.ToDictionary(x => x.Key, x => x.Value);
First you are union both of collections, after that group them by the keys selects the Last().Value for the value and converting it again to Dictionary.
Here Full example

Transform a dictionary to another with a different key type

Given:
class KeyType1{...}
class KeyType2{...}
class ValueType{...}
public KeyType2 Lookup(KeyType1 value)
{
// Returns a unique value of KeyType2 or otherwise, null
}
Is there a neat LINQ way to transform:
IDictionary<KeyType1,ValueType> ==> IDictionary<KeyType2,ValueType>
The following important points:
No 2 values of type KeyType1 map to the same KeyType2
edit It is possible Lookup may return null i.e. the mapping may not be complete. In such cases, these entries should be omitted from the new dictionary
ok, assuming Lookup is a function that returns a reference type that could be null.
b = a
.Select(p => new { Key = Lookup(p.Key), p.Value }))
.Where(p => p.Key != null)
.ToDictionary(p => p.Key, p => p.Value);
You can use Where firstly to filter the source, then use ToDictionary extension specifying the new key and the value:
var dictionary1 = new Dictionary<KeyType1, ValueType>();
var dictionary2 = dictionary1.Where(kv => Lookup(kv.Key) != null)
.ToDictionary(kv => Lookup(kv.Key), kv => kv.Value);
If you want to compute the key for those cases where Lookup is null you can do this:
dictionary1.ToDictionary(kv => Lookup(kv.Key) ?? ComputeNewKey(kv.Key),
kv => kv.Value);
To avoid to need Lookup twice you can firstly create a Dictionary<KeyType1, KeyType2> to map the old keys to the new ones:
IDictionary<KeyType1,KeyType2> keyMap = sourceDictionary.Keys.ToDictionary(key => key, key => Lookup(key));
This keyMap now may contain null values for some source keys. So we filter it with Where and then combine them with the source dictionary to create your new dictionary:
IDictionary<KeyType2,ValueType> target = keyMap.Where(kvp => kvp.Value != null).
ToDictionary(kvp => kvp.Value, sourceDictionary[kvp.Key]);
Dictionary<string, string> StringDict = new Dictionary<string, string>();
Dictionary<int, string> IntDict = new Dictionary<int, string>();
StringDict.ToList().ForEach(D =>
{
IntDict.Add(Convert.ToInt32(D.Key), D.Value);
});

How to use ToDictionary with <string, object> dictionary

I am trying to create a dictionary that can hold multiple values per key, and I have created an class called Pair that consists of two strings. I have defined idDictionary to contain a string as the key, and Pair as the value, but I am unsure how to write the ToDictionary statement as this concept is new to me, and I couldn't find any examples of this.
Dictionary<string, Pair<string, string>> idDictionary = new Dictionary<string, Pair<string, string>>();
I know with a regular generic Dictionary of I would simply use something like this:
idDictionary = resultData.Rows.Select(row => row.Split(','))
.ToDictionary(id => id[0], id => id[1]);
I am not sure how I would implement something similar for the object called Pair. Maybe I'm missing something really simple, but many thanks to those with answers.
EDIT to include full code block and more thorough explanation
The original code block is here (with a generic dictionary). The reason I am changing this is due to the fact that if there is more than 1 value per key, the application errors out due to duplicate keys.
private List<ImportItem<T>> ProcessReportResult(CSVTable resultData, ICollection<ImportItem<T>> data, Func<T, string> keyFilter)
{
WriteLog("{1}{0} records found.{1}", resultData.Rows.Length, Environment.NewLine);
//key = Order Number; value = Order ID
var idDictionary = resultData.Rows.Select((row => row.Split(','))).ToDictionary(id => id[0], id => id[1]);
idDictionary.ForEach(id => WriteLog("Input Id = {0} - Matching record Id = {1}", id.Key, id.Value));
var processList = data.Where(item => idDictionary.ContainsKey(keyFilter(item.DataItem))).ToList();
processList.ForEach(item => item.id = idDictionary[keyFilter(item.DataItem)]);
return processList;
}
A genral solution to get the one to many key value store can be achieved via grouping but that would require to have value as List of items. If I try to explain it via your given sample then the query to convert the rows to per key multi value store can be created by:
idDictionary =
resultData.Rows
.GroupBy(row => row.Id, row => row.Split(','))
.ToDictionary(g => g.Key, g => g.ToList());
Update:
Specific solution to your problem. Assuming that data would have structure something like:
List<Row> rows = new List<Row>{
new Row{
values = "1,A"
},
new Row{
values = "2,C,D,E"
},
new DataRow{
values = "3,E,X,CV,B"
},
};
You can use the Group here as well to get the Key, Value(List). Note, Here I have skipped the first value which is already captured as key at index 0.
var idDictionary =
rows.GroupBy(row => row.values.Split(',')[0],
row => row.values.Split(',').Skip(1))
.ToDictionary(g => g.Key, g => g.ToList());
This will give you the result like:
/* output -
|1, (A)|
|2, (C,D,E)|
|3, (E,X,CV,B)|
*/
Though you have to change the implementation for fetching the values via List.
But this solution will prevent the Program if there are more than one values found per key.
Not sure exactly what you need maybe this simple example will help?
idDictionary = resultData.Rows
.Select((row => row.Split(',')))
.ToDictionary<string, Pair<string, string>>
(id => id[0],id => new Pair(id[1],id[1]));
This version of ToDictionary takes two functions, one that returns the key and one that returns the value for each item in the enumeration.
You'll have to decide whether you want a tuple-based approach (or pair even) if you know how many items are in each row or if you need to consider that each row may have a different number of items.
// Setup sample data
var resultData = new
{
Rows = new string[] { "1,A,B,C", "2,A,B", "3,A,B,C,D" }
};
// If same length for each row, tuple would work easily
// Dictionary<string, Tuple<string, string>>
var tuples = resultData.Rows
.Select(r => r.Split(','))
.ToDictionary(
r => r[0],
r => Tuple.Create(r[1], r[2])
);
// If length is variable, then some type of collection could be better
// Dictionary<string, List<string>>
var lists = resultData.Rows
.Select(r => r.Split(','))
.ToDictionary(
r => r[0],
r => r.Skip(1).ToList() // Skip adding id element
);
Here is the output for the 1st item to compare each:
?lists["1"]
Count = 3
[0]: "A"
[1]: "B"
[2]: "C"
?tuples["1"]
{(A, B)}
Item1: "A"
Item2: "B"
The original code block is here (with a generic dictionary). The reason I am changing this is due to the fact that if there is more than 1 value per key, the application errors out due to duplicate keys.
Seems like what you are looking for is ToLookup
"Lookup<TKey, TElement>
represents a collection of keys each mapped to one or more values."
.
var idDictionary = resultData.Rows.Select((row => row.Split(',')))
.ToLookup(id => id[0], id => id[1]);
EDIT
A short sample:
var lines = new string[] { "a,b", "a,c", "d,e" };
var dict = lines.Select(line => line.Split(','))
.ToLookup(x => x[0], x => x[1]);
result:
Key: a Value: [b,c]
Key: e Value: [e]
Sample usage:
Console.WriteLine(string.Join(",", dict["a"]));

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

Categories

Resources