c# if key exist in dictionary still add the value in dictionary - c#

From a datatable I am fetching the value and putting in a Dictionary<string,string>:
Dictionary<string, string> mydic= new Dictionary<string, string>();
my datatable for ex is
Value RowOrder
page1 01
page2 00
page3 00
I am using LINQ to fetch the RowOrder according to value given and putting into mydic:
string id = (from DataRow dr in table3.Rows where (string)dr["Value"] == formula
select (string)dr["RowOrder"]).FirstOrDefault();
mydic.Add(id,Value);
If I run this, error is showing:
"An item with the same key has already been added."
How to overcome this. I want page1, page2, page3 should be added with values 01, 00, 00 respectively

You need to check whether the dictionary already has the key before adding to it:
if(!mydic.ContainsKey(id))
{
mydic.Add(id, Value);
}
A dictionary cannot contain two items with the same key, if you are expecting duplicate id values you need to consider using a different data structure.
Perhaps a List<Tuple<string, string>>?

Check if key exists before adding
if (mydic.ContainsKey(id))
mydic[id] = Value; // or throw exception
else
mydic.Add(id, Value);
BTW if you want to convert your DataTable to Dictionary<string, string> with RowOrder as key, and first (or last) Value as value, you can use LINQ:
var mydic = table3.AsEnumerable()
.GroupBy(r => r.Field<string>("RowOrder"))
.Select(g => g.First()) // or last to use last value for key
.ToDictionary(r => r.Field<string>("RowOrder"),
r.Field<string>("Value"));

You can use GroupBy, here a single line approach using Linq-To-DataSet:
Dictionary<string, string> mydic = table3.AsEnumerable()
.GroupBy(row => row.Field<string>("Value"))
.ToDictionary(grp => grp.Key, grp => grp.First().Field<string>("RowOrder"));

Use
mydic[id] = Value;
instead of mydic.Add();
The Add method should be used if you want to ensure only one item with a given key is inserted.
Note that this overwrite the previously written value.
If you want to have more items with the same key you should use a
Dictionary<string, IList<string>>
or some other datastructure I don't know of but I would be very glad to hear about since I used that kind of dictionary more than once

You may use a Lookup<string,string>:
List<Tuple<string, string>> tuples = new List<Tuple<string, string>>();
tuples.Add(new Tuple<string, string>("01", "page1"));
tuples.Add(new Tuple<string, string>("00", "page2"));
tuples.Add(new Tuple<string, string>("00", "page3"));
var lookup = tuples.ToLookup(t => t.Item1,t=> t.Item2 );
And you may use it like:
var result = lookup["00"];
foreach (var item in result)
{
Console.WriteLine(item);
}
Which outputs:
page2
page3

Related

Merge two Dictionary<string, string> in asymmetric way

I have two dictionary in C# 6.0 and I'd like to merge them in a smart way.
Take the first dictionary foo as:
var foo = new Dictionary<string, string>
{
{"a", "10"},
{"b", "20"},
{"c", "30"},
};
And the second dictionary bar as:
var bar = new Dictionary<string, string>
{
{"a", "333"},
{"e", "444"},
{"f", "555"},
};
I'd like to merge them in one dictionary with this logic:
If a key is in foo but not in bar ignore it in the new dictionary
If a key is in bar but not in foo, take it in the new dictionary
if a key is in both foo and bar, take the value of foo in the new dictionary
Here is my expected result:
var result = new Dictionary<string, string>
{
{"a", "10"}, //this comes from foo
{"e", "444"}, //this comes from bar
{"f", "555"}, //this comes from bar
};
Is there a smart way to handle this problem without forloop (LINQ expressions are fine)?
You could use HashSet<T> methods and LINQ:
1)
var fooKeys = new HashSet<string>(foo.Keys);
var barKeys = new HashSet<string>(bar.Keys);
fooKeys.IntersectWith(barKeys); // remove all from fooKeys which are not in both
barKeys.ExceptWith(fooKeys); // remove all from barKeys which are remaining in fooKeys and also in barKeys
Dictionary<string, string> result = fooKeys
.Select(fooKey => new KeyValuePair<string, string>(fooKey, foo[fooKey]))
.Concat(barKeys.Select(bKey => new KeyValuePair<string, string>(bKey, bar[bKey])))
.ToDictionary(kv => kv.Key, kv => kv.Value);
This is safe because both exclude each other. It's also very efficient since these HashSet methods have O(n) complexity with two sets.
If it's not understandable in your opinion, maybe you like this more:
2)
var inBoth = from kv1 in foo
join kv2 in bar
on kv1.Key equals kv2.Key
select kv1;
var onlyInBar = bar.Keys.Except(foo.Keys)
.Select(b => new KeyValuePair<string, string>(b, bar[b]));
Dictionary<string, string> result = new Dictionary<string, string>();
foreach (var kv in inBoth.Concat(onlyInBar))
result.Add(kv.Key, kv.Value);
The first query uses a join(more readable in query-syntax) which only returns keyvalue-pairs from the first dictionary where the key also exists in the second dictionary. The second query uses Enumerable.Except to exclude all from the second dictionary which are in the first. Both, Enumerable.Join and Enumerable.Except use sets under the hood so they are very efficient.
It's worth noting that due to LINQ's deferred execution both queries are executed only at the foreach (var kv in inBoth.Concat(onlyInBar)) and not before.
Probably the easiest and most readable approach, a "LINQ left-outer-join":
3)
KeyValuePair<string, string> defaultPair = default(KeyValuePair<string, string>);
var query = from barKv in bar
join fooKv in foo
on barKv.Key equals fooKv.Key into gj_bf
from bf in gj_bf.DefaultIfEmpty()
select bf.Equals(defaultPair) ? barKv : bf;
foreach (var kv in query)
result.Add(kv.Key, kv.Value);
You can use GroupJoin like this:
var res =
bar
.GroupJoin(
foo,
kvp => kvp.Key,
kvp => kvp.Key,
(kvp, g) => new KeyValuePair<string, string>(kvp.Key, g.FirstOrDefault().Value ?? kvp.Value))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
The trick here is to GroupJoin bar with foo! this way, everything from bar will appear in the final result and for same keys the joined result will be an IEnumerable of matched results from second collection which in your case is foo and since it is a Dictionary so the matched results will contain only one element and all you need to do is to get its value. In case of no match (items in bar but not in foo) the matched results collection will be empty, so FirstOrDefault() will return the default value of KeyValuePair<string, string> with Key and Value both set to null. So in this case we just get the Value from our first collection (in your case bar).
Your logic can be simplified as:
The result will contain all keys from bar, with value taken from foo if exists, otherwise from bar.
which translates to something like this:
var result = bar.ToDictionary(barItem => barItem.Key, barItem =>
foo.ContainsKey(barItem.Key) ? foo[barItem.Key] : barItem.Value);
or a bit longer, but more optimal:
var result = bar.ToDictionary(barItem => barItem.Key, barItem =>
{ string fooValue; return foo.TryGetValue(barItem.Key, out fooValue) ? fooValue : barItem.Value; });
A (simple) Linq solution:
var newDict = new Dictionary<string, string>();
var toIncludeFromFoo = bar.Keys.Intersect(foo.Keys).ToList();
toIncludeFromFoo.ForEach(x => newDict [x] = foo[x]);
var toAddFromBar = bar.Keys.Except(foo.Keys).ToList();
toAddFromBar.ForEach(x => newDict [x] = bar[x]);

List of KeyValuePair to be Distinct

I have a list of KeyValuePair which its values are list too such as
List<KeyValuePair<string, List<string>>> ListX = new List<KeyValuePair<string,List<string>>>();
ListX.Add(new KeyValuePair<string,List<string>>("a",list1));
ListX.Add(new KeyValuePair<string,List<string>>("b",list1));
ListX.Add(new KeyValuePair<string,List<string>>("a",list1));`
I want the keys of each KeyValuePair in the list to be not duplicated, only the keys, can I use Distinct in this list?
for example I want the third item in the list that has "a" key to be deleted because it's duplicated.
Though it is possible to work around with your current List to make it having Distinct keys, the simplest solution which I think fit for your case is to use Dictionary<string,List<string>>
It does just exactly what you need:
Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();
dict.Add("a", new List<string>());
dict.Add("b", new List<string>());
dict.Add("a", new List<string>()); //will throw an error
Image:
If you need to check if a Key is already exist when you want to add a <Key,Value> to a your dictionary, simply check by ContainsKey:
if (dict.ContainsKey(key)) //the key exists
var dictionaryX = ListX
.GroupBy(x => x.Key, (x, ys) => ys.First())
.ToDictionary(x => x.Key, x => x.Value);
I'm not sure if this is what you were looking for, but it's a query that will convert a ListX into a dictionary by only taking the first value for each duplicate key.
You can use class Dictionary<TKey, TValue> which inherits from IEnumerable<KeyValuePair<TKey, TValue>>. It is a collection of KeyValuePairs which allows only unique keys.
U can use
Dictionary<TKey, TValue>
where Tkey and Tvalue are generic datatypes.
For example they can be int, string,another dictionary etc.
ExampleDictionary<int , string>, Dictionary<int , List<employee>> etc.
In all these cases the key is the distinct part ie, same key cannot be inserted again.
U can check if key exists using Distinct so that no exception occurs even if u try to add same key
However Distinct prevents only same key value pairs .
To prevent same key being added use Enumerable.GroupBy
ListItems.Select(item =>
{
long value;
bool parseSuccess = long.TryParse(item.Key, out value);
return new { Key = value, parseSuccess, item.Value };
})
.Where(parsed => parsed.parseSuccess)
.GroupBy(o => o.Key)
.ToDictionary(e => e.Key, e => e.First().Value)
List<Dictionary<int, List<int>>> list = new List<Dictionary<int, List<int>>>(); //List with a dictinary that contains a list
int key = Convert.ToInt32(Console.ReadLine()); // Key that you want to check if it exist in the dictinary
int temp_counter = 0;
foreach(Dictionary<Int32,List<int>> dict in list)
{
if(dict.ContainsKey(key))
temp_counter+=temp_counter;
}
if (temp_counter == 0) // key not present in dictinary then add a to the list a dictinary object that contains your list
{
Dictionary<int,List<int>> a = new Dictionary<int,List<int>>();
a.Add(key,new List<int>()); // will contain your list
list.Add(a);
}
Check if this works

Remap Lookup keys in C# using LINQ

I have my Lookup looking like this:
Lookup<string, DataModel> lookup;
Besides that I have dictionary containing key mappings:
Dictionary<string, KeyModel> keyMappings;
What I want to do is to re-map string keys in lookup to the KeyModel entities in the following way:
Lookup <string, DataModel> lookup;
||
||
Dictionary<string, KeyModel> keyMappings;
___________|
v
Lookup<KeyModel, DataModel> result;
Given:
Dictionary<string, KeyModel> keyMappings = ...;
ILookup<string, DataModel> lookup = ...;
the response is:
ILookup<KeyModel, DataModel> lookup2 = lookup
.SelectMany(x => x, (grouping, element) => new { Key = keyMappings[grouping.Key], Element = element })
.ToLookup(x => x.Key, x => x.Element);
So you first re-linearize the ILookup<,> by using SelectMany, and then recreate the ILookup<,>.
Clearly you need your KeyModel to define the GetHashCode() and the Equals(), or you need an IEqualityComparer<KeyModel> to pass as the parameter of the ToLookup()
You can use a simple foreach-loop.
First you need to transform your Lookup into a Dictionary. See here.
foreach (KeyValuePair<string, DataModel> kvp in lookup) {
KeyModel outValue;
if (keyMappings.TryGetValue(kvp.Key, out outValue))
{
result.Add(outValue, lookup[kvp.Key]);
}}
Not the fastest solution, maybe someone is coming up with a nice LINQ.

Add keyvaluepair from file to Dictionary?

I am trying to import values from a .txt file into my dictionary. The .txt file is formatted like this:
Donald Duck, 2010-04-03
And so on... there is 1 entry like that on each line. My problem comes when I try to add the split strings into the dictionary.
I am trying it like this: scoreList.Add(values[0], values[1]); But it says that names doesn't exist in the context. I hope someone can point me in the correct direction about this...
Thank you!
private void Form1_Load(object sender, EventArgs e)
{
Dictionary<string, DateTime> scoreList = new Dictionary<string, DateTime>();
string path = #"list.txt";
var query = (from line in File.ReadAllLines(path)
let values = line.Split(',')
select new { Key = values[0], Value = values[1] });
foreach (KeyValuePair<string, DateTime> pair in scoreList)
{
scoreList.Add(values[0], values[1]);
}
textBox1.Text = scoreList.Keys.ToString();
}
Your values variable are only in scope within the LINQ query. You need to enumerate the query result, and add the values to the dictionary:
foreach (var pair in query)
{
scoreList.Add(pair.Key, pair.Value);
}
That being said, LINQ features a ToDictionary extension method that can help you here. You could replace your loop with:
scoreList = query.ToDictionary(x => x.Key, x => x.Value);
Finally, for the types to be correct, you need to convert the Value to DateTimeusing, for instance, DateTime.Parse.
First you are doing it wrong, you should add item from list not values[0] and values[1] used in LINQ..
Dictionary<string, DateTime> scoreList = new Dictionary<string, DateTime>();
string path = #"list.txt";
var query = (from line in File.ReadAllLines(path)
let values = line.Split(',')
select new { Key = values[0], Value = values[1] });
foreach (var item in query) /*changed thing*/
{
scoreList.Add(item.Key, DateTime.Parse(item.Value)); /*changed thing*/
}
textBox1.Text = scoreList.Keys.ToString();
The immediate problem with the code is that values only exists in the query expression... your sequence has an element type which is an anonymous type with Key and Value properties.
The next problem is that you're then iterating over scoreList, which will be empty to start with... and there's also no indication of where you plan to convert from string to DateTime. Oh, and I'm not sure whether Dictionary<,>.Keys.ToString() will give you anything useful.
You can build the dictionary simply enough though:
var scoreList = File.ReadLines(path)
.Select(line => line.Split(','))
.ToDictionary(bits => bits[0], // name
bits => DateTime.ParseExact(bits[1], // date
"yyyy-MM-dd",
CultureInfo.InvariantCulture));
Note the use of DateTime.ParseExact instead of just DateTime.Parse - if you know the format of the data, you should use that information.

Most elegant way to convert string array into a dictionary of strings

Is there a built-in function for converting a string array into a dictionary of strings or do you need to do a loop here?
Assuming you're using .NET 3.5, you can turn any sequence (i.e. IEnumerable<T>) into a dictionary:
var dictionary = sequence.ToDictionary(item => item.Key,
item => item.Value)
where Key and Value are the appropriate properties you want to act as the key and value. You can specify just one projection which is used for the key, if the item itself is the value you want.
So for example, if you wanted to map the upper case version of each string to the original, you could use:
var dictionary = strings.ToDictionary(x => x.ToUpper());
In your case, what do you want the keys and values to be?
If you actually just want a set (which you can check to see if it contains a particular string, for example), you can use:
var words = new HashSet<string>(listOfStrings);
You can use LINQ to do this, but the question that Andrew asks should be answered first (what are your keys and values):
using System.Linq;
string[] myArray = new[] { "A", "B", "C" };
myArray.ToDictionary(key => key, value => value);
The result is a dictionary like this:
A -> A
B -> B
C -> C
IMO, When we say an Array we are talking about a list of values that we can get a value with calling its index (value => array[index]), So a correct dictionary is a dictionary with a key of index.
And with thanks to #John Skeet, the proper way to achieve that is:
var dictionary = array
.Select((v, i) => new {Key = i, Value = v})
.ToDictionary(o => o.Key, o => o.Value);
Another way is to use an extension method like this:
public static Dictionary<int, T> ToDictionary<T>(this IEnumerable<T> array)
{
return array
.Select((v, i) => new {Key = i, Value = v})
.ToDictionary(o => o.Key, o => o.Value);
}
If you need a dictionary without values, you might need a HashSet:
var hashset = new HashSet<string>(stringsArray);
What do you mean?
A dictionary is a hash, where keys map to values.
What are your keys and what are your values?
foreach(var entry in myStringArray)
myDictionary.Add(????, entry);
I'll assume that the question has to do with arrays where the keys and values alternate. I ran into this problem when trying to convert redis protocol to a dictionary.
private Dictionary<T, T> ListToDictionary<T>(IEnumerable<T> a)
{
var keys = a.Where((s, i) => i % 2 == 0);
var values = a.Where((s, i) => i % 2 == 1);
return keys
.Zip(values, (k, v) => new KeyValuePair<T, T>(k, v))
.ToDictionary(kv => kv.Key, kv => kv.Value);
}
Dictionary<int, string> dictionaryTest = new Dictionary<int, string>();
for (int i = 0; i < testArray.Length; i++)
{
dictionaryTest.Add(i, testArray[i]);
}
foreach (KeyValuePair<int, string> item in dictionaryTest)
{
Console.WriteLine("Array Position {0} and Position Value {1}",item.Key,item.Value.ToString());
}
The Question is not very clear, but Yes you can convert a string to Dictionary provided the string is delimited with some characters to support Dictionary<Key,Value> pair
So if a string is like a=first;b=second;c=third;d=fourth you can split it first based on ; then on = to create a Dictionary<string,string> the below extension method does the same
public static Dictionary<string, string> ToDictionary(this string stringData, char propertyDelimiter = ';', char keyValueDelimiter = '=')
{
Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
Array.ForEach<string>(stringData.Split(propertyDelimiter), s =>
{
if(s != null && s.Length != 0)
keyValuePairs.Add(s.Split(keyValueDelimiter)[0], s.Split(keyValueDelimiter)[1]);
});
return keyValuePairs;
}
and can use it like
var myDictionary = "a=first;b=second;c=third;d=fourth".ToDictionary();
since the default parameter is ; & = for the extension method.
You can create a dictionary from an IEnumerable<T>, including an array, via:
var dictionary = myEnumerable.ToDictionary(element => element.Key,
element => element.Value)
where Key and Value are the key and value you want to store in each dictionary element. Available in .NET Framework 3.5+/.NET Core 1.0+/.NET 5.0+. Official documentation.
If you want the dictionary values to be the elements from the original enumerable:
var dictionary = myEnumerable.ToDictionary(element => element.Key)
If you only need high-performance set operations, you may be able to use:
var words = new HashSet<string>(listOfStrings);
In simple terms, the HashSet class can be thought of as a Dictionary<TKey,TValue> collection without values. Official documentation.
(Note that a 'sequence' in an entirely unrelated object.
Originally submitted an existing answer edit but it was rejected by the author so posting separately, including with links to the official Microsoft documentation.)

Categories