How do I use linq to fill dictionary with default values? - c#

Dictionary1 has a key of entities to a string, let's say "Def3".
So it looks like:
Ent1, Def3
Ent3, Def3
Dictionary2 has all entities associated to another string, that string which is not important.
Ent1, Unimportant
Ent2, Unimportant
Ent3, Unimportant
I know a default string Def2 I'd like to put into Dictionary1 for every Entity in Dictionary2 that doesn't exist in Dictionary1.
How can I update Dictionary1 such that it looks like:
Ent1, Def3
Ent2, Def2
Ent3, Def3
Fyi: These are short examples for much larger dictionaries, so simple case-by-case insertion wouldn't work here.
Update: Ok, let me clarify. If Dictionary2 Has a Key that is not a key in Dictionary1, add Dictionary2's Key with a string ("Def2").

foreach(var key in Dictionary2.Keys.Where(k => !Dictionary1.Keys.Contains(k)))
{
Dictionary1.Add(key, defaultstring);
}

You can use LINQ for this job:
var Dictionary3 = Dictionary2.ToDictionary(x => x.Key,
x => Dictionary1.ContainsKey(x.Key) ? Dictionary1[x.Key] : "Def2");

So you want all keys of Dictionary2 that are no keys in Dictionary1 to be added as a key in Dictionary1 with a value def2.
In small steps:
var allKeysInDict1 = Dictionary1.Keys;
var allKeysInDict2 = Dictionary2.Keys;
var missingKeysInDict1 = allKeysInDict2.Except(allKeysInDict1);
foreach (var missingKey in missingKeysInDict1)
{
dictionary1.Add(missingKey, def2);
}

Related

How to get max key that contains partial value in dictionary in unity

This is for Unity (C#)
I have a this Dictionary :
public Dictionary<int, string> lvl_dict = new Dictionary<int, string>()
{
//Dict tuplas index (Unity), moduleStepCode (API)
{1,"R1_G1_A1_M1_A"},
{2,"R1_G1_A1_M1_P"},
{3,"R1_G1_A1_M1_E"},
{4,"R1_G1_A1_M2_A"},
{5,"R1_G1_A1_M2_P"},
{6,"R1_G1_A1_M2_E"},
{7,"R1_G1_A1_M3_A"},
{8,"R1_G1_A1_M3_P"},
I need to retrieve the maximum Key given a partial Value.
Example, If I input "R1_G1_A1_M2", I need to retrieve Key = 6.
I've seen answers that retrieve the Max key given a Value, but not a partial value, which should create a subset of the dictionary, and then retrieve the max key, I guess? But I don't know how to do that.
I tried using LINQ
var maxkey = lvl_dict.Where(item => item.Value.Contains("R1_G1_A1_M2").max() );
Debug.Log("max key is : " + maxkey);
But clearly i don't know what i am doing.
There is two way to get max of keys:
1- using Max function
var max = lvl_dict.Where(x => x.Value.Contains("G1_A1_M2")).Select(x => x.Key).Max();
2- using MaxBy function
var max = lvl_dict.Where(x => x.Value.Contains("G1_A1_M2")).MaxBy(x => x.Key).Key;

How to merge 2 Dictionaries into a third that consists of {Key=Value(Dict1), Value=Value(Dict2)}

I am trying to merge 2 Dictionaries.
The first one consists of an ID and a Title, so for instance: {2, Error}.
The second one consists of an ID and the frequency in which the ID occurs: {2, 3}.
I want to create a dictionary that looks like this: {Error, 3}.
Is this possible?
My code thus far works only if there are no duplicates ... I want to just add the frequency number of the duplicate ID to the already saved frequency of the previous occurrence of the same ID. So e.g. if in dict2 for some reason there exists {2, 3} and {2, 5} the result in dict3 should be {Error, 8}.
foreach (var item in dict2)
{
foreach(var entry in dict1)
{
if (item.Key.Equals(entry.Key))
{
dict3.Add(entry.Value.ToString(), (int)item.Value);
}
}
}
You can use LINQ:
var combinedDictQry =
from entry1 in dictionary1
from entry2 in dictionary2
where entry1.Key == entry2.Key
select new { Key = entry1.Value, Value = entry2.Value };
var combinedDict = combinedDictQry.ToDictonary(entry => entry.Key, entry => entry.Value);
This will serve you well for smaller dictionaries (ie. for most practical purposes). If you need to process larger amounts of data, you might get performance issues and you will have to access the values of the second dictionary with TryGetValue to make use of the dictionary's better access performance.
If you expect duplicate values in dictionary1, .ToDictionary will not work, because duplicate keys are not allowed in a dictionary. You can however create a lookup. Duplicate keys will be merged and the values can be enumerated:
var combinedLookup = combinedDictQry.ToLookup(entry => entry.Key, entry => entry.Value);

String parsing C# creating segments?

I have a string in the form of:
"company=ABCorp, location=New York, revenue=10million, type=informationTechnology"
I want to be able to parse this string out and get "name", "value" pairs in the form of
company = ABCCorp
location= New York etc.
This could be any suitable data structure to store. I was thinking maybe a Dictionary<string, string>() but im open to suggestions.
Is there a suitable way of doing this in C#?
EDIT: My final goal here is to have something like this:
Array[company] = ABCCorp.
Array[location] = New York.
What data structure could we use to achieve the above? MY first thought is a Dictionary but I am not sure if Im missing anything.
thanks
Using String.Split and ToDictionary, you could do:
var original = "company=ABCorp, location=New York, revenue=10million, type=informationTechnology";
var split = original.Split(',').Select(s => s.Trim().Split('='));
Dictionary<string,string> results = split.ToDictionary(s => s[0], s => s[1]);
string s = "company=ABCorp, location=New York, revenue=10million, type=informationTechnology";
var pairs = s.Split(',')
.Select(x => x.Split('='))
.ToDictionary(x => x[0], x => x[1]);
pairs is Dictionary with the key value pair. The only caveat is you will probably want to deal with any white space between the comma and the string.
It depends a lot on the expected syntax. One way to do this is to use String.Split:
http://msdn.microsoft.com/en-us/library/system.string.split(v=vs.110).aspx
First split on comma, then iterate over all items in the string list returned and split those on equality.
However, this requires that comma and equality are never present in the values?
I'm assuming a weak RegEx/LINQ background so here's a way to do it without anything "special".
string text = "company=ABCorp, location=New York, revenue=10million, type=informationTechnology";
string[] pairs = text.Split(',');
Dictionary<string, string> dictData = new Dictionary<string, string>();
foreach (string currPair in pairs)
{
string[] data = currPair.Trim().Split('=');
dictData.Add(data[0], data[1]);
}
This has the requirement that a comma (,) and an equal-sign (=) never exist in the data other than as delimiters.
This relies heavily on String.Split.

C# Linq .ToDictionary() Key Already Exists

Final Edit: I was able to locate the duplicate field in the ini file. Thanks for your help everyone!
I'm using a regular expression to parse an ini file and LINQ to store it in a Dictionary:
Sample Data:
[WindowSettings]
Window X Pos='0'
Window Y Pos='0'
Window Maximized='false'
Window Name='Jabberwocky'
[Logging]
Directory='C:\Rosetta Stone\Logs'
EDIT: Here is the file actually causing the problem: http://pastebin.com/mQSrkrcP
EDIT2: I've narrowed it down to being caused by the last section in the file: [list_first_nonprintable]
For some reason one of the files that I'm parsing with this is throwing this exception:
System.ArgumentException: An item with the same key has already been added.
Is there any way for me to either find out which key is causing the problem (so I can fix the file), or to just skip the key that's causing this and continue parsing?
Here is the code:
try
{
// Read content of ini file.
string data = System.IO.File.ReadAllText(project);
// Create regular expression to parse ini file.
string pattern = #"^((?:\[)(?<Section>[^\]]*)(?:\])(?:[\r\n]{0,}|\Z))((?!\[)(?<Key>[^=]*?)(?:=)(?<Value>[^\r\n]*)(?:[\r\n]{0,4}))*";
//pattern = #"
//^ # Beginning of the line
//((?:\[) # Section Start
// (?<Section>[^\]]*) # Actual Section text into Section Group
// (?:\]) # Section End then EOL/EOB
// (?:[\r\n]{0,}|\Z)) # Match but don't capture the CRLF or EOB
// ( # Begin capture groups (Key Value Pairs)
// (?!\[) # Stop capture groups if a [ is found; new section
// (?<Key>[^=]*?) # Any text before the =, matched few as possible
// (?:=) # Get the = now
// (?<Value>[^\r\n]*) # Get everything that is not an Line Changes
// (?:[\r\n]{0,4}) # MBDC \r\n
// )* # End Capture groups";
// Parse each file into a Dictionary.
Dictionary<string, Dictionary<string, string>> iniFile
= (from Match m in Regex.Matches(data, pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline)
select new
{
Section = m.Groups["Section"].Value,
kvps = (from cpKey in m.Groups["Key"].Captures.Cast<Capture>().Select((a, i) => new { a.Value, i })
join cpValue in m.Groups["Value"].Captures.Cast<Capture>().Select((b, i) => new { b.Value, i }) on cpKey.i equals cpValue.i
select new KeyValuePair<string, string>(cpKey.Value, cpValue.Value)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
}).ToDictionary(itm => itm.Section, itm => itm.kvps);
return iniFile;
}
catch (ArgumentException ex)
{
System.Diagnostics.Debug.Write(ex.ToString());
return new Dictionary<string, Dictionary<string, string>>();
}
Thanks in advance.
This just means that when you convert to a Dictionary --
.ToDictionary(itm => itm.Section, itm => itm.kvps);
-- there are multiple keys (itm.Section). You can use ToLookup instead, which is kind of like a dictionary but allows multiple keys.
Edit
There are a couple of ways to call ToLookup. The simplest is to specify the key selector:
var lookup =
// ...
.ToLookup(itm => itm.Section);
This should provide a lookup where the key is of type Group. Getting a lookup value should then return an IEnumerable, where T is the anonymous type:
Group g = null;
// TODO get group
var lookupvalues = lookup[g];
If the .NET compiler doesn't like this (sometimes it seems to have trouble figuring out what the various types should be), you can also specify an element selector, for example:
ILookup<string, KeyValuePair<string,string>> lookup =
// ...
.ToLookup(
itm => itm.Section.Value, // key selector
itm => itm.kvps // element selector
);
You can write your own ToDictionary method that doesn't break with duplicate keys easy enough.
public static Dictionary<K,V> ToDictionary<TSource, K, V>(
this IEnumerable<TSource> source,
Func<TSource, K> keySelector,
Funct<TSource, V> valueSelector)
{
//TODO validate inputs for null arguments.
Dictionary<K,V> output = new Dictionary<K,V>();
foreach(TSource item in source)
{
//overwrites previous values
output[keySelector(item)] = valueSelector(item);
//ignores future duplicates, comment above and
//uncomment below to change behavior
//K key = keySelector(item);
//if(!output.ContainsKey(key))
//{
//output.Add(key, valueSelector(item));
//}
}
return output;
}
I assume that you could figure out how to implement the additional overloads (without value the selector).
You can use Tuple to pass multiple keys. Check sample code below:
.ToDictionary(k => new Tuple<string,string>(k.key1,k.key2), v => v.value)

Extracting unique keys from key/value pairs, and grouping the values in an array

Let's say I have a List<NameValuePair>, where NameValuePair is a simple object that has a Name property and a Value property, both strings.
The list is populated with values like this:
name = "name1", value = "value1"
name = "name1", value = "value2"
name = "name2", value = "value3"
name = "name3", value = "value4"
Note that there are two instances of the "name1" key. There can be any number of keys (since this is a List).
I want to turn this List into a new list, which has just unique keys, and groups any values with the same key name as an array/list of that key.
So the above should become:
name = "name1", value = "value1", "value2" // value is a string array or list
name = "name2", value = "value3"
name = "name3", value = "value4"
What is the easiest way to accomplish this?
The easiest way is with a ILookup, which is essentially like a dictionary but can have more than one value for each key.
You can do something like this to create your lookup:
var lookup = list.ToLookup(pair => pair.name,
pair => pair.value);
Then you could print the name/value pairs like this:
foreach (var nameGroup in lookup)
{
var name = nameGroup.Key;
foreach (string value in nameGroup)
Console.WriteLine("Name: {0}, Value: {1}", name, value);
}
Maybe with a Dictionary<string,List<string>> you could do something like
for (var kv in mylistofnamed) {
if (!dict.ContainsKey(kv.Key))
dict[kv.Key] = new List<string>();
dict[kv.Key].Add(kv.Value);
}
?
If you only need a read-only collection then Lookup will do the trick, as in Meta-Knight's answer.
If you need to modify the collection after its initial creation/population, then you probably need something like Dictionary<string, List<string>>. You can create and populate such a dictionary from your original list using LINQ:
var dict = list
.GroupBy(x => x.Name)
.ToDictionary(x => x.Key, y => y.Select(z => z.Value).ToList());
One way is with a dictionary:
http://arcanecode.com/2007/03/05/dictionaries-in-c-the-hashtable/
A HashTable can do what you need with unique keys. You will have to supply a List as the value however, since you will be storing multiple values per key.
Here's another easy example:
http://dotnetperls.com/hashtable-keys
You will need to iterate over each KeyValuePair in your list to populate the HashTable by storing the name as the key and the values as the value. Because you may have a name that points to multiple values, you will need your values in the HashTable to be Lists.
Check for the existence of the name in the HashTable, if it's not there, create a new list for that name and add the value to the list. If the key already exists, access that element in the HashTable and add the new value to the list that maps to the key.
All classes that implement the IDictionary interface or the Generic IDictionary Interface enforce uniqueness checks for the Keys. You could use any of the classes, though I confess my preference for the Generic Dictionary<TKey, TValue> class.
When adding values, you can just check if the Dictionary object already contains the supplied Key. If not, then you can add the item into the Dictionary.

Categories