Is there a way to have a Dictionary in C# that will automatically have an empty list associated with any key, other than creating such a class myself? I would like to avoid the following code if possible:
int x = 0;
int y = 42;
Dictionary<int, List<int>> dict = new Dictionary<int, List<int>>();
List<int> newList;
if (dict.containsKey(x))
{
dict[x].Add(y);
}
else
{
dict[x] = new List<int>{y};
}
or possibly:
int x = 0;
int y = 42;
Dictionary<int, List<int>> dict = new Dictionary<int, List<int>>();
List<int> newList;
if (dict.TryGetValue(x, out newList))
{
newList.Add(y);
}
else
{
dict[x] = new List<int>{y};
}
This behavior can be abstracted nicely with an extension method.
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory)
{
if (dictionary == null) throw new ArgumentNullException(nameof(dictionary));
if (valueFactory == null) throw new ArgumentNullException(nameof(valueFactory));
TValue value;
if (!dictionary.TryGetValue(key, out value))
{
value = valueFactory.Invoke(key);
dictionary.Add(key, value);
}
return value;
}
The method signature is identical to ConcurrentDictionary.GetOrAdd().
var list = dict.GetOrAdd(x, (_) => new List<int>());
list.Add(y);
Usually my code is:
if (!dict.containsKey(x))
dict[x] = new List<int>();
dict[x].Add(y);
I believe it's easier to read
you already have answer, dictionary does not have such ability, using extension method just fakes it and hides the fact that you are looking up dictionary twice (in case key doesn't exist). but you will write one line of code if that makes you happy.
In c#7 how ever you can pack things more a little bit.
if (dict.TryGetValue(x, out var list)) list.Add(y);
else dict.Add(x, new List<int>{y});
this will have one lookup if key already exist, and two lookups if key doesn't exist.
Related
I am working on .NET CORE 6. I have nested Dictionary<int, string> where I am adding value in loop to nested dictionary from string. The string holds multiple record where each record holds 15 columns hence dictionary is useful to hold keys.
After 15 iteration, I add nested/ child dictionary to parent Dictionary<string, string> and then I set nested dictionary to start again from counter 0. The issue is once I clear nested dictionary using Clear(), it also clear data from Parent dictionary. I need help on this and how I can approach to avoid that, dataItemDictionary.Clear();
private Dictionary<string, Dictionary<int, string>> ConvertStream(string stream)
{
Dictionary<string, Dictionary<int, string>> dataDictionary = new Dictionary<string, Dictionary<int, string>>();
Dictionary<int, string> dataItemDictionary = new Dictionary<int, string>();
if (!string.IsNullOrWhiteSpace(stream))
{
string column = string.Empty;
int dataItemDictionaryindex = 0;
int dataDictionaryIndex = 0;
int columnCounter = 16;
foreach(char c in stream)
{
if(c.Equals('|'))
{
dataItemDictionary.Add(dataItemDictionaryindex, column);
column = string.Empty;
dataItemDictionaryindex++;
if (columnCounter == dataItemDictionaryindex)
{
Dictionary<int, string> data = new Dictionary<int, string>();
data = dataItemDictionary; // need help here...
dataDictionary.Add("Record#" + dataDictionaryIndex, data);
dataItemDictionary.Clear();
dataDictionaryIndex++;
columnCounter = columnCounter * (dataDictionaryIndex + 1);
}
}
else
{
if (!c.Equals('\r') && !c.Equals('\n'))
{
column = column + c;
}
}
}
}
return dataDictionary;
}
Dictionary<TKey, TValue> is a reference type. Use the copy constructor of Dictionary<TKey, TValue> to create a new collection instance.
Dictionary<int, string> data = new Dictionary<int, string>(dataItemDictionary);
I'm new working with c#, i have experience working with c++ and java. I'm trying to mess around with dictionaries but i cant really get this to work. I have two arrays the data type have to be objects, after i add them to two different dictionaries im trying to find a key within, but i cant get it to go into the if statements.Which of the two declarations of dictionary is correct dictionary1 or dictionary2? Also how can i find a value by the key or a key by the value in the correct dictionary or both.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Practice_With_Dictionaries
{
class Program
{
static void Main(string[] args)
{
object[] array1 = new object[5];
array1[0] = "1111";
array1[1] = "2222";
array1[2] = "3333";
array1[3] = "4444";
array1[4] = "5555";
object[] speed = new object[5];
speed[0] = 1;
speed[1] = 2;
speed[2] = 3;
speed[3] = 4;
speed[4] = 5;
object[] keys = new object[1];
keys[0] = (object[])array1;
object[] speedTable = new object[1];
speedTable[0] = (object[])speed;
Dictionary<object, object> dictionary1 = new Dictionary<object, object>();
Dictionary<object[], object[]> dictionary2 = new Dictionary<object[], object[]>();
dictionary1.Add(keys, speedTable);
dictionary2.Add(keys, speedTable);
if (dictionary1.ContainsKey((object)"1111"))
{
var method = 1;
}
if (dictionary2.ContainsKey(array1))
{
var method = 2;
}
}
}
}
dictionary1.ContainsKey((object)"1111") will never return true because "1111" will be boxed into a new unique object every time.
Populate one item at a time
You can populate the dictionary one item at a time:
Dictionary<object, object> dictionary1 = new Dictionary<object, object>();
for (int i = 0; i < array1.Length; i++)
{
dictionary1.Add(array1[i], speed[i]);
}
object key1 = array1[0];
if (dictionary1.ContainsKey(key1))
{
var method = 1;
}
Populate using LINQ
You can also populate the dictionary without explicit loops using LINQ and the ToDictionary(IEnumerable<TSource, Func<TSource, TKey>, Func<TSource, TElement>) method, which creates a Dictionary from an IEnumerable according to specified key selector and element selector functions.
Dictionary<object, object> dictionary2 = array1
.Select((obj, index) => new KeyValuePair<object, object>(array1[index], speed[index]))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Dictionary<object, object> dictionary3 = array1
.Select((obj, index) => index)
.ToDictionary(i => array1[i], i => speed[i]);
Dictionary<object, object> dictionary4 = Enumerable.Range(0,5)
.ToDictionary(i => array1[i], i => speed[i]);
The challenge with your code is that you are passing key values in a form of array or in others, they are using a list. Usually we initialize a Dictionary in a one to one relationship:
Dictionary<object, object> dict = new Dictionary<object, object>();
Sometimes in a one to many relationship:
Dictionary<object, object[]> dict = new Dictionary<object, object[]>();
In your case, you initialize it on a many to many relationship:
Dictionary<object[], object[]> dictionary2 = new Dictionary<object[], object[]>();
Although on your first example, particularly dictionary1, you still pass an array on your TKey value (see your code):
Dictionary<object, object> dictionary1 = new Dictionary<object, object>();
dictionary1.Add(keys, speedTable); //the value of keys consists of an object of an array: keys[0] = (object[])array1;
So your best shot is to implement your Dictionary with TKey of a single object, and TValue of an object or an array of object or a list of objects.
If you still want to do an array of object, you need to implement a custom IEqualityComparer since you cannot do what you are trying to do in your if statements.
Here is a sample generic implementation, you need to supply IEqualityComparer in your constructor Dictionary:
public class DictionaryComparer<T> : IEqualityComparer<List<T>>
{
public bool Equals(List<T> x, List<T> y)
{
//TODO: Your implementation for your contains or equals condition
}
public int GetHashCode(List<T> obj)
{
//TODO: Implementation of your GetHashCode
}
}
Then implement it:
if (new DictionaryComparer<object>().Equals(lstCompare, lstCompareTo))
{
//TODO: Your condition here..
}
My array dictionary key value pairs are similar to this
[0]
key : x
value : 1
[1]
key : y
value : 2
[2]
key : z
value : 3
But I need to reverse the index of the dictionary. I need to convert the above dictionary to this :
[0]
key : z
value : 3
[1]
key : y
value : 2
[2]
key : x
value : 1
I tried Reverse() function. But it didn't work. I don't know how to achieve this. Can anyone help me with this ?
How can I achieve this ?
You shouldn't assume dictionaries are ordered. They aren't.
If you want to have an array that is ordered, you should use a SortedDictionary. You can also reverse the order in there if you want to. You should use a custom comparer for that (altered from here):
class DescendedStringComparer : IComparer<string>
{
public int Compare(string x, string y)
{
int ascendingResult = Comparer<string>.Default.Compare(x, y);
// turn the result around
return 0 - ascendingResult;
}
}
//
SortedDictionary<string, string> test
= new SortedDictionary<string, string>(new DescendedDateComparer());
You can iterate over it with foreach for example. The results will be ordered descending.
the function Enumerable.Reverse() returns an IEnumerable not an array. Did you forget to make the reversed sequence an array? The following worked for me:
class Program
{
static void Main(string[] args)
{
var dict = new Dictionary<string, int>();
dict.Add("x", 1);
dict.Add("y", 2);
dict.Add("z", 3);
var reversed = dict.ToArray().Reverse();
var reversedArray = reversed.ToArray();
Console.ReadKey();
}
}
I tried reverse method, it worked. Please see below images. I used VS 2013, .Net 4.5
Output generated
You should use SortedDictionary and pass a key on construction
public sealed class CompareReverse<T> : IComparer<T>
{
private readonly IComparer<T> original;
public CompareReverse(IComparer<T> original)
{
this.original = original;
}
public int Compare(T left, T right)
{
return original.Compare(right, left);
}
}
and in your main call
var mydictionary = new SortedDictionary<int, string>(
new CompareReverse<int>(Comparer<int>.Default));
for eg:-
var mydictionary = new SortedDictionary<int, string>(
new CompareReverse<int>(Comparer<int>.Default));
dictionary.add(1, "1");
dictionary.add(2, "2");
dictionary.add(3, "3");
dictionary.add(4, "4");
SortedDictionary sorts by key, not by value.
Here's a clean way to reverse the order of values in a Dictionary:
List<KeyValuePair<string, string>> srcList = aDictionary.ToList();
srcList.Sort(
delegate(KeyValuePair<string, string> firstPair,
KeyValuePair<string, string> nextPair)
{
return firstPair.Value.CompareTo(nextPair.Value);
}
);
Dictionary<string, string> outputDictionary = new Dictionary<string, string>();
int listCount = srcList.Count;
for(int i = 0; i < listCount; i++) {
int valueIndex = i + 1;
outputDictionary.Add(srcList[i].Key, srcList[valueIndex]);
}
If what you want as output is a List of KeyValuePairs rather than a Dictionary, you can replace the final 6 lines of code in my example with this:
List<KeyValuePair<string, string>> destList = new List<KeyValuePair<string, string>>();
int listCount = srcList.Count;
for(int i = 0; i < listCount; i++) {
int valueIndex = i + 1;
destList.Add(new KeyValuePair<string, string>(srcList[i].Key, srcList[valueIndex]));
}
You can do this :
for (var i = dictionary.Count - 1; i >= 0; i--)
dictionary.Values.ElementAt(i), dictionary.Keys.ElementAt(i);
EDIT : Go here -> how to iterate a dictionary<string,string> in reverse order(from last to first) in C#?
I have dictionary and I need get duplicate values.
For example:
Dictionary<int, List<string>> dictionary = new Dictionary<int, List<string>>();
List<string> list1 = new List<string> { "John", "Smith" };
List<string> list2 = new List<string> { "John", "Smith" };
List<string> list3 = new List<string> { "Mike", "Johnson" };
dictionary.Add(1, list1);
dictionary.Add(2, list2);
dictionary.Add(3, list3);
I need find all duplicate from dictionary and return max keys(collection of key) of each duplicate values. From my test dictionary I need return list with only one key = 2
Maybe I chose the wrong data structure. I would like to receive optimal algorithm
With your current structure, you're in a bit of trouble because you don't necessarily have an easy way to compare two List<string> to see if they are equal.
One way to work around this is to create a custom List<string> comparer that implements IEqualityComparer<List<string>>. However, since you have a list of strings, we also need to reorder both to ensure that we are comparing each value in the correct order. This affects the cost of your algorithm. On the other hand, if you are happy with the order of the values inside of the lists, that works just fine as well and you can avoid that cost.
public class StringListComparer : IEqualityComparer<List<string>>
{
public bool Equals(List<string> x, List<string> y)
{
return CompareLists(x, y);
}
public int GetHashCode(List<string> obj)
{
return base.GetHashCode();
}
private static bool CompareLists(List<string> x, List<string> y)
{
if (x.Count != y.Count)
return false;
// we HAVE to ensure that lists are in same order
// for a proper comparison
x = x.OrderBy(v => v).ToList();
y = y.OrderBy(v => v).ToList();
for (var i = 0; i < x.Count(); i++)
{
if (x[i] != y[i])
return false;
}
return true;
}
}
Once we have our comparer, we can use it to pull out keys from subsequent duplicates, leaving the first key (per your requirement).
public List<int> GetDuplicateKeys(Dictionary<int, List<string>> dictionary)
{
return dictionary
.OrderBy (x => x.Key)
.GroupBy(x => x.Value, new StringListComparer())
.Where (x => x.Count () > 1)
.Aggregate (
new List<int>(),
(destination, dict) =>
{
var first = dict.FirstOrDefault();
foreach (var kvp in dict)
{
if (!kvp.Equals(first))
destination.Add(kvp.Key);
}
return destination;
}
).ToList();
}
The following test outputs keys 2 and 4.
Dictionary<int, List<string>> dictionary = new Dictionary<int, List<string>>();
dictionary.Add(1, new List<string> { "John", "Smith" });
dictionary.Add(2, new List<string> { "John", "Smith" });
dictionary.Add(3, new List<string> { "Mike", "Johnson"});
dictionary.Add(4, new List<string> { "John", "Smith" });
var result = GetDuplicateKeys(dictionary);
You could create a list of KeyValuePair<int, List<string>>, that contains the ordered lists, with the outer list sorted. Then you could find duplicates very quickly. You'd need a list comparer that can compare ordered lists.
class MyListComparer: Comparer<List<string>>
{
public override int Compare(List<string> x, List<string> y)
{
for (var ix = 0; ix < x.Count && ix < y.Count; ++ix)
{
var rslt = x[ix].CompareTo(y[ix]);
if (rslt != 0)
{
return rslt;
}
}
// exhausted one of the lists.
// Compare the lengths.
return x.Count.CompareTo(y.Count);
}
}
var comparer = new MyListComparer();
var sortedList = dictionary.Select(kvp =>
new KeyValuePair<int, List<string>>(kvp.Key, kvp.Value.OrderBy(v => v))
.OrderBy(kvp => kvp.Value, comparer)
.ThenBy(kvp => kvp.Key);
Note the ThenBy, which ensures that if two lists are equal, the one with the smaller key will appear first. This is necessary because, although OrderBy does a stable sort, there's no guarantee that enumerating the dictionary returned items in order by key.
// the lists are now sorted by value. So `"Smith, John"` will appear before `"Smith, William"`.
// We can go through the list sequentially to output duplicates.
var previousList = new List<string>();
foreach (var kvp in sortedList)
{
if (kvp.Value.SequenceEqual(previousList))
{
// this list is a duplicate
// Lookup the list using the key.
var dup = dictionary[kvp.Key];
// Do whatever you need to do with the dup
}
else
{
previousList = kvp.Value;
}
}
This sorts each list only once. It does use more memory, because it duplicates the dictionary in that list of KeyValuePair<int, List<string>>, but for larger data sets it should be much faster than sorting each list multiple times and comparing it against every other list.
Caveats: The code above assumes that none of the lists are null, and none of them are empty. If a list in the dictionary can be null or empty, then you'll have to add some special case code. But the general approach would be the same.
i need to read data from some excel sheets. The data in the excel sheets is already formatted in such a way that i am able to get the desired data by using this method.
This is what i am doing:
using (var conn = new OleDbConnection(strBld.ToString()))
{
conn.Open();
IEnumerable<IDictionary<string, object>> excelDataRaw =
conn.Query("select * from [Curves$A:IT]").
Cast<IDictionary<string, object>>();
int i = 0;
string previousKey = null;
var curve = new List<IEnumerable<object>>();
var excelData = new Dictionary<string, IDictionary<object, object>>();
//var excelData = new Dictionary<string, IDictionary<string, decimal>>();
foreach (var key in excelDataRaw.Select(dictionary => dictionary.Keys).
ElementAt(i))
{
string key1 = key;
// gets the data from one column within the excel file
curve.Add(excelDataRaw.Select(col => col[key1]).
Where(row => row != null).ToList());
if (i % 2 == 0)
{
// store the column header
previousKey = key;
}
if (i % 2 == 1)
{
// merge the data from the first column (keys)
// with the data from the second column (values)
IEnumerable<object> keys = curve[i - 1];
IEnumerable<object> values = curve[i];
// cast works but than you can't zip the lists together
//IEnumerable<string> keys = curve[i - 1].Cast<string>();
//IEnumerable<decimal> values = curve[i].Cast<decimal>();
// zip them together on their index
var dic = keys.Zip(values, (k, v) => new { k, v }).
ToDictionary(x => x.k, x => x.v);
if (previousKey != null)
{
if (!excelData.ContainsKey(previousKey))
{
excelData.Add(previousKey, dic);
}
}
}
++i;
}
}
I extract all data from the excel file (excelDataRaw). i then select all data that belongs together into a list (curve) and combine two lists that belong to each other into a dictionary (dic). The final result is a dictionary (excelData) that contains the column head from the excel file as a key (previousKey) and the data relevant to this column head as a dictionary (dic).
I would like to cast the dictionary (excelData) from
Dictionary<string, IDictionary<object, object>>
into
Dictionary<string, IDictionary<string, decimal>>
but i can't cast object into string or decimal and i can't zip the lists together to get the dictionary (dic) after casting each list to the desired type. Has anyone an idea how to achieve the desired result(type)?
ExcelDataRaw is of type Dictionary<string, IDictionary<object, object>> so it needs IDictionary<object, object> as the value. You can't cast Dictionary<string,decimal> into IDictionary<object,object> because IDictionary is not a covariant interface - see http://msdn.microsoft.com/en-us/library/dd469487.aspx.
The solution is either to change the type of ExcelDataRaw to Dictionary<string, IDictionary<string, decimal>> or to keep it as is and when trying to use the values from that dictionary cast those to correct types:
foreach(var kv in ExcelDataRaw)
{
Dictionary<string,decimal> convertedValue=kv.Value.ToDictionary(x=>(string)x.Key,x=>(decimal)x.Value);
// or convert it even further down the road
string lookup = "abc";
decimal v = (decimal)kv.Value[lookup];
}
Well i found a solution, I must have overlooked the obvious yesterday. A good night of sleep helps sometimes :).
IEnumerable<object> keys = curve[i - 1];
IEnumerable<object> values =
curve[i].Where(content => decimal.TryParse(content.ToString(), out num));
Dictionary<string, decimal> dic =
keys.Zip(values, (k, v) => new { k, v }).ToDictionary(
x => (string)x.k, x => decimal.Parse(x.v.ToString()));
if (previousKey != null)
{
if (!excelData.ContainsKey(previousKey))
{
excelData.Add(previousKey, dic);
}
}