How to modify key in a dictionary in C# - c#

How can I change the value of a number of keys in a dictionary.
I have the following dictionary :
SortedDictionary<int,SortedDictionary<string,List<string>>>
I want to loop through this sorted dictionary and change the key to key+1 if the key value is greater than a certain amount.

As Jason said, you can't change the key of an existing dictionary entry. You'll have to remove/add using a new key like so:
// we need to cache the keys to update since we can't
// modify the collection during enumeration
var keysToUpdate = new List<int>();
foreach (var entry in dict)
{
if (entry.Key < MinKeyValue)
{
keysToUpdate.Add(entry.Key);
}
}
foreach (int keyToUpdate in keysToUpdate)
{
SortedDictionary<string, List<string>> value = dict[keyToUpdate];
int newKey = keyToUpdate + 1;
// increment the key until arriving at one that doesn't already exist
while (dict.ContainsKey(newKey))
{
newKey++;
}
dict.Remove(keyToUpdate);
dict.Add(newKey, value);
}

You need to remove the items and re-add them with their new key. Per MSDN:
Keys must be immutable as long as they are used as keys in the SortedDictionary(TKey, TValue).

You can use LINQ statment for it
var maxValue = 10
sd= sd.ToDictionary(d => d.key > maxValue ? d.key : d.Key +1, d=> d.Value);

If you don't mind recreating the dictionary, you could use a LINQ statment.
var dictionary = new SortedDictionary<int, SortedDictionary<string, List<string>>>();
var insertAt = 10;
var newValues = dictionary.ToDictionary(
x => x.Key < insertAt ? x.Key : x.Key + 1,
x => x.Value);
return new SortedDictionary<int, SortedDictionary<string, List<string>>>(newValues);
or
var dictionary = new SortedDictionary<int, SortedDictionary<string, List<string>>>();
var insertAt = 10;
var newValues = dictionary.ToDictionary(
x => x.Key < insertAt ? x.Key : x.Key + 1,
x => x.Value);
dictionary.Clear();
foreach(var item in newValues) dictionary.Add(item.Key, item.Value);

Related

How to separate the string and value to dictionary?

I have a comma separated string with values as follows:
"File_name,cost_per_page,0.23,color_code,343,thickness,0.01".
I want to read cost_per_page = 0.23, color_code=343 and thickness=0.01.
How can I do this? I could do this by putting it to a list and reading successive element next to key string. Is there any other method?
The simplest (and therefor, probably the best) approach would be to simply use string.Split and then iterate the array:
var source = "File_name,cost_per_page,0.23,color_code,343,thickness,0.01";
var splitted = source.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries)
var result = new Dictionary<string, string>();
// Note: Starting from 1 to skip the "file_name"
// moving 2 indexes in each iteration,
// and ending at length - 2.
for(int i = 1; i < splitted.Length - 1; i+=2)
{
result.Add(splitted[i], splitted[i+1]);
}
I've tried to find a clever way to do it with linq, but the best I came up with is really not that clever at all:
var valuesWithIndexes = source
.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries)
.Skip(1)
.Select((v, i) => new {v, i});
var keys = valuesWithIndexes.Where(x => x.i % 2 == 0);
var values = valuesWithIndexes.Where(x => x.i % 2 == 1);
var dictionary = keys.Zip(values, (k, v) => new {k, v})
.ToDictionary(key => key.k,
val => val.v);
I think the simple for loop is a clear winner in this case.
I think that this is the simplest and best approach.
string str = "File_name,cost_per_page,0.23,color_code,343,thickness,0.01";
string[] array = str.Split(',');
Dictionary<string, double> dict = new Dictionary<string, double>();
for (int i = 1; i < array.Length - 1; i = i + 2)
{
string key = array[i];
double value = Double.Parse(array[i + 1], CultureInfo.InvariantCulture);
dict.Add(key, value);
}
You can also use the above code for a larger string (variable str has more key-value pairs).
I'd use linq.
Convert the string into a delimited array.
Set up a dictionary containing the first item...not sure if you want to do this. If not just remove the "d1" and "union" statements.
Run some Linq to create your dictionary based on even numbers.
Then if you really want to account for the first item, file_name, and you want that at the beginning of your dictionary then lastly you'd run the "union" statement.
string str = "File_name,cost_per_page,0.23,color_code,343,thickness,0.01";
string[] array = str.Split(',');
Dictionary<string, string> d1 = new Dictionary<string, string>()
{
{ array[0], "" }
};
Dictionary<string, string> d2 = array.Select((i, index) => (index > 0 && index % 2 == 0) ?
new { key = array[index - 1], value = i } : null).Where(i => i != null).ToDictionary(d => d.key, d => d.value);
Dictionary<string, string> result = d1.Union(d2).ToDictionary(k => k.Key, v => v.Value);
foreach (KeyValuePair<string, string> kvp in result)
Console.WriteLine(kvp.Key + ": " + kvp.Value);
return;
another, and probably cleaner approach:
This accounts for File_name as well. If you don't want it then add "index > 0" to the conditional operator where it checks for even.
string str = "File_name,cost_per_page,0.23,color_code,343,thickness,0.01";
string[] array = str.Split(',');
Dictionary<string, string> d2 = array.Select((i, index) => (index % 2 == 0) ?
new
{
key = (index == 0) ? i : array[index - 1],
value = (index == 0) ? "" : i
} : null).Where(i => i != null).ToDictionary(d => d.key, d => d.value);
foreach (KeyValuePair<string, string> kvp in d2)
Console.WriteLine(kvp.Key + ": " + kvp.Value);
Full working example in this fiddle.
All you need is to iterate while you haven't reached your string array end ensuring your index increments by 2 (field name, field value).
int i = 1;
var temp = s.Split(',').ToList();
while(i < temp.Count) {
i = i+2;
}

Search in dictionary of dictionaries

I have a dictionary defined as seen here:
Dictionary<int, Dictionary<string, object>> dict = new Dictionary<..>();
And a sample code for adding data:
dict.Add (X, new Dictionary<string, object> ());
dict [X].Add ("Car", CarObject);
dict [X].Add ("Seller", SellerObject);
dict [X].Add ("Key3", Z);
I want to search in the inner dictionary if it contains an object which then contains the following
CarObject.Name = (wildcard)X1(wildcard) of the key "Car" but I just can't seem to get a grasp of how to get into the inner dictionary and then into the object with LINQ in order to search for the value.
This will return all matching KeyValuePair<string, object>.
var query = dict.SelectMany(d => d.Value)
.Where(i => i.Key == "Key1"
&& (
i.Value is CarObject
? ((CarObject)i.Value).Name.Contains("X1")
: false
));
Try the following:
var results = dict[X].Where(x => (x.Value is CarObject) && ((CarObject)x.Value).Name.Contains("X1"));
If you want to get just the value and not the dictionary, and print the values, you can do this:
int X = 0, Z = 1;
dict[X].Add("Key1", CarObject);
dict[X].Add("Key2", SellerObject);
dict[X].Add("Key3", Z);
var results = dict[X].Where(x => (x.Value is CarObject) && ((CarObject)x.Value).Name.Contains("X1")).Select(x => x.Value);
foreach (var str in results)
Console.WriteLine(str);
You can try something like:
dict[X].Where(x => x.Value is CarObject && ((CarObject)x.Value).Name.Contains("X1"));
Or:
dict[X].Values.OfType<CarObject>().Any(x => x.Name.Contains("X1"))

Join two dictionaries by value diffs

I have these two dictionaries:
Dictionary<char, double> analyzed_symbols = new Dictionary<char, double>();
Dictionary<char, double> decode_symbols = new Dictionary<char, double>();
I need to create another dictionary that should have their keys as key and value, like this:
Dictionary<char, char> replace_symbols = new Dictionary<char, char>();
The condition to "join" them is that difference between values should be minimal, like this:
Math.Min(Math.Abs(analyzed_symbols[key] - decode_symbols[key]))
I guess I should use LINQ for this purpose but can't figure out how to write query properly.
Data Sample:
analyzed_symbols = [a, 173], [b, 1522], [z, 99]
decode_symbols = [в, 100], [д, 185], [e, 1622]
For these dicts output data should look like this:
replace_symbols = [z, в], [b, е], [a, д]
I've found question that is pretty close to what I need, but not exactly. Snowy asks there about one close value, but I need to do the same thing for two dictionaries.
This is my take on it:
var analyzed_symbols = new Dictionary<char, double>(){ {'a', 173}, {'b', 1522}, {'z', 99} };
var decode_symbols = new Dictionary<char, double>(){ {'в', 100}, {'д', 185}, {'e', 1622} };
var q = from a in analyzed_symbols
from d in decode_symbols
let tmp = new { A = a.Key, D = d.Key, Value = Math.Abs(a.Value - d.Value) }
group tmp by tmp.A into g
select new
{
Key = g.Key,
Value = g.OrderBy (x => x.Value).Select (x => x.D).First()
};
var replace_symbols = q.ToDictionary (x => x.Key, x => x.Value);
Okay, I'll try. I divided into several queries, because it's more readable that way.
//sorting values of the dictionaries to easily get closest
var analyzedSortedValues = analyzed_symbols.Values.OrderBy(k => k);
var decodeSortedValues = decode_symbols.Values.OrderBy(k => k);
//creating pairs of the closest values. Here I use iterator index i to skip
//some values that have been used already (is it correct?)
var t = analyzedSortedValues.Select((k, i) => new { a = k, d = decodeSortedValues.Skip(i).Any() ? decodeSortedValues.Skip(i).First() : -1 });
//printing results by getting appropriate keys from corresponding dictionaries
foreach (var item in t)
{
Console.WriteLine("[{0}, {1}]", analyzed_symbols.FirstOrDefault(kvp => kvp.Value == item.a).Key, decode_symbols.FirstOrDefault(kvp => kvp.Value == item.d).Key);
}
I am not exactly sure how to do it via LINQ but here is the longhand version of what you want to do.
private static Dictionary<char, char> BuildReplacementDictionary(Dictionary<char, double> analyzedSymbols,
Dictionary<char, double> decodeSymbols)
{
Dictionary<char, char> replaceSymbols = new Dictionary<char, char>(analyzedSymbols.Count);
foreach (KeyValuePair<char, double> analyzedKvp in analyzedSymbols)
{
double bestMatchValue = double.MaxValue;
foreach (KeyValuePair<char, double> decodeKvp in decodeSymbols)
{
var testValue = Math.Abs(analyzedKvp.Value - decodeKvp.Value);
if (testValue <= bestMatchValue)
{
bestMatchValue = testValue;
replaceSymbols[analyzedKvp.Key] = decodeKvp.Key;
}
}
}
return replaceSymbols;
}
What it does is it goes through each element of the analyzed dictionary, test every element of the decoded dictionary, and if that match is the same or better than the previous match it found it will use the new value from the decoded dictionary.

How to get below format of nested dictionary?

I have two dictionaries
One is nested dictionary -
Dictionary<string, List<string>> Dict = new Dictionary<string, List<string>>();
And another is normal one –
Dictionary<string, string> ObjDict = new Dictionary<string, string>();
In normal dictionary I have values like this
{[DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_1.xml, 0000000047510D9744C9A54EB11C0]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_2.xml, 0000000047510D9744C9A54EB11C0]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_1.xml, 0000000047510D9744C9A54EB11C1]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_2.xml, 0000000047510D9744C9A54EB11C1]}
{[DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_3.xml, 0000000047510D9744C9A54EB11C2]}
Now I want the nested dictionary like this –
“Key0” DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_1.xml
DateTime_7_25_2013_12_26_11_PM_Table_2_1_Trade_2_2.xml
“Key1” DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_1.xml
DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_2.xml
“Key2” DateTime_7_25_2013_12_26_11_PM_Table_2_2_Trade_3_3.xml
All the keys of equal values of normal dictionary should belongs to one key of nested dictionary.
Please suggest.
ObjDict.GroupBy(kvp => kvp.Value).ToDictionary(g => g.Key, g => g.Select(kvp => kvp.Key).ToList())
Ok, if I understand the question correctly:
Given you have an input dictionary where you want to group the items by the values in the dictionary and put them into a new dictionary where each key is one of the values from the original dictionary, and each value is a list of the keys with that value:
var items = new Dictionary<string, string>
{
{"C03", "C"},
{"B01", "B"},
{"A01", "A"},
{"C02", "C"},
{"A03", "A"},
{"B03", "B"},
{"B02", "B"},
{"A02", "A"},
{"C01", "C"}
};
var result = items.GroupBy(item => item.Value)
.ToDictionary
(
g => g.Key,
g => g.Select(x => x.Key).ToList()
);
foreach (var item in result)
{
Console.Write("Values for key " + item.Key + ": ");
foreach (var value in item.Value)
Console.Write(value + " ");
Console.WriteLine();
}
The above code produces the following output:
Values for key C: C03 C02 C01
Values for key B: B01 B03 B02
Values for key A: A01 A03 A02
As you can see, this output is not ordered (it's in the same order as the input).
If you want to order both the keys in the dictionary, and the values in each list, you can do so like this:
var result = items.GroupBy(item => item.Value)
.OrderBy(item => item.Key)
.ToDictionary
(
g => g.Key,
g => g.Select(x => x.Key).OrderBy(x => x).ToList()
);
This change produces this output:
Values for key A: A01 A02 A03
Values for key B: B01 B02 B03
Values for key C: C01 C02 C03
you can use that:
foreach (var item in ObjDict)
{
if (Dict.ContainsKey(item.Value))
{
var e = Dict[item.Value];
e.Add(item.Key);
}
else
{
Dict.Add(item.Value, new List<string>() { item.Key });
}
}
you could create your own Dictionary, MultiDictionary,
in the Multipledictionary, maintain a private dictionary with key and List
when add the value, check if key has already exists, if exists, add to the list
and you can make it Generic
public class MultipleDictionary<TKey, TValue>
{
private IDictionary<TKey, IList<TValue>> _dict;
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public TValue this[TKey key]
{
get
{
if (_dict.ContainsKey(key))
return _dict[key][0];
return default(TValue);
}
set
{
if (_dict.ContainsKey(key))
{
IList<TValue> valList = _dict[key];
valList.Add(value);
}
else
{
IList<TValue> list = new List<TValue>();
list.Add(value);
_dict.Add(key, list);
}
}
}
}
this is the idea, try implement the rest yourself
A bit of pseudocode, so you can do some work on your own :P
step 1:
make a hashTable or something which stores your ObjDict.Values and your Dict.Keys
( "0000000047510D9744C9A54EB11C0" -> "Key0" )
step2:
for each ObjDict.Key
if(Dict.contains(HashTable[thisKey])
Take List from Dict and add the ObjDict.Value
else
Make new list, add ObjDict.Value
Dict.Add(HashTable[thiskey], theList

Checking a dictionary if there are same values for a key

I have a dictionary object like this:
CustomKeys<int, string>
eg;
1000, F1
1001, F2
1002, F1
1003, F4
1004, F2
I want to know if I have more than 1 of same values in this dictionary. I would also want to keep a note of which keys(unique id) has duplicates.
Is that possible?
It is possible using GroupBy and than Count() > 1 to keep track of which values that have duplicates.
var q = dic.GroupBy(x => x.Value)
.Select (x => new { Item = x, HasDuplicates = x.Count() > 1 });
You can find all key values they had the same values like this;
Dictionary<int, string> d = new Dictionary<int, string>();
d.Add(1000, "F1");
d.Add(1001, "F2");
d.Add(1002, "F1");
d.Add(1003, "F4");
d.Add(1004, "F2");
var dublicate = d.ToLookup(x => x.Value, x => x.Key).Where(x => x.Count() > 1);
foreach (var i in dublicate)
{
Console.WriteLine(i.Key);
}
Here is a DEMO.
But if you want to get a boolean value since your item's has a same value, look at Magnus's answer which is great.
I'm not sure by what you mean by "keeping note of which has duplicate values". If you mean keeping note of the keys, you could do this:
var keys = new Dictionary<int, string>();
keys.Add(1000, "F1");
keys.Add(1001, "F2");
keys.Add(1002, "F1");
keys.Add(1003, "F4");
keys.Add(1004, "F2");
var duplicates = keys.GroupBy(i => i.Value).Select(i => new
{
keys = i.Select(x => x.Key),
value = i.Key,
count = i.Count()
});
foreach (var duplicate in duplicates)
{
Console.WriteLine("Value: {0} Count: {1}", duplicate.value, duplicate.count);
foreach (var key in duplicate.keys)
{
Console.WriteLine(" - {0}", key);
}
}
If you mean keeping track of the duplicate values only, see Sonor's answer.
Another solution could be:
var duplicates = dictionary.GroupBy( g => g.Value )
.Where( x => x.Count( ) > 1 )
.Select( x => new { Item = x.First( ), Count = x.Count( ) } )
.ToList( );

Categories