I want to find a key in a dictionary and replace the value if it is found or add the key/value if it is not.
Code:
public class MyObject
{
public string UniqueKey { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
LINQ Solution (throws An item with the same key has already been added.
):
Dictionary<string, MyObject> objectDict = csvEntries.ToDictionary(csvEntry => csvEntry.ToMyObject().UniqueKey, csvEntry => csvEntry.ToMyObject());
ForEach solution (works):
Dictionary<string, MyObject> objectDict = new Dictionary<string, MyObject>();
foreach (CSVEntry csvEntry in csvEntries)
{
MyObject obj = csvEntry.ToMyObject();
if (objectDict.ContainsKey(obj.UniqueKey))
{
objectDict[obj.UniqueKey] = obj;
}
else {
objectDict.Add(obj.UniqueKey, obj);
}
}
I really liked the LINQ solution but as it stands, it throws the above error. Is there any nice way of avoiding the error and using LINQ?
You can use GroupBy
to create unique keys:
Dictionary<string, MyObject> objectDict = csvEntries
.Select(csvEntry => csvEntry.ToMyObject())
.GroupBy(x => x.UniqueKey)
.ToDictionary(grp => grp.Key, grp => grp.First());
However, instead of grp.First()
you could create a collection with ToList
or ToArray
. On that way you don't take an arbitary object in case of duplicate keys. Or add your priority-logic in an OrderBy
before First
: grp => grp.OrderBy(x => Field1).ThenBy(x => x.Field2).First()
Another option is to use a Lookup<TKey, TValue>
which allows duplicate keys and even non-existing keys, you get an empty sequence in that case.
var uniqueKeyLookup = csvEntries
.Select(csvEntry => csvEntry.ToMyObject())
.ToLookup(x => x.UniqueKey);
IEnumerable<MyObject> objectsFor1234 = uniqueKeyLookup["1234"]; // empty if it doesn't exist