Can't retrieve data from list<keyvaluepair> - c#

List<KeyValuePair<String, String> myList = new List<KeyValuePair<String, String>>();
myList.Add(new KeyValuePair<String, SelectList>("theKey", "FIND THIS!"));
How can I retrieve "FIND THIS!" from myList knowing only theKey? This attempt is not working.
String find = myList.Where(m => m.Key == "theKey");
Coming from other languages, I've always had the possibility to search in big associative arrays and retrieve values like this: array[key] = value;
How can I do this in C#?

Instead of List<KeyValuePair>, use Dictionary<string, SelectList> and then you can access it like :
array[key] = value;
You can use Dictionary like:
Dictionary<String, SelectList> dictionary= new Dictionary<String, SelectList>();
dictionary.Add("theKey", "FIND THIS!");
Console.WriteLine(dictionary["theKey"]);

You are probably looking for the Dictionary<TKey, TValue>:
Dictionary<string, string> myDict = new Dictionary<string, string>();
myDict.Add("theKey", "FIND THIS!");
now you can find the value via the key:
string value = myDict["theKey"];
You can change the value in this way:
myDict["theKey"] = "new value"; // works even if the key doesn't exist, then it will be added
Note that the keys must be unique.

How about Dictionary ?
IDictionary<String, String> foo = new Dictionary<String, String>();
foo.Add("hello","world");
now you can use []
foo["Hello"];
however with C#
string value;
if(foo.TryGetValue("Hello" , out value)){
// now you have value
}
is much more preferable and safer.

As mentioned in other answers you should use a Dictionary for this.
However, the reason your line String find = myList.Where(m => m.Key == "theKey"); is not working is that myList.Where(m => m.Key == "theKey"); will return a KeyValuePair. If you just want the value you could try:
String find = myList.Where(m => m.Key == "theKey").Single().Value;
or if you need to check for nulls then maybe:
var findKeyValue = myList.Where(m => m.Key == "theKey").SingleOrDefault();
if(findKeyValue != null)
{
var find = findKeyValue.Value;
}
You can also use the following snippet (in which case you'll either have the value or null)
var find = myList.Where(m => m.Key == "theKey").Select(kvp => kvp.Value).SingleOrDefault();

Related

Return a List from Tuple of dual values via Linq query

I have a list of tuples with dual values:
List<Tuple<string, string>> Descriptions;
And I keep adding content to this, like so:
Descriptions.Add (new Tuple<string, string> ("max", "some description"));
Descriptions.Add (new Tuple<string, string> ("joe", "some description"));
Descriptions.Add (new Tuple<string, string> ("jane", "some description"));
Descriptions.Add (new Tuple<string, string> ("max", "some other description"));
I want to retrieve a list using Linq where Item1 in the tuple is a certain value like, say, "max". I could use this code:
var s = Descriptions.Where (x => x.Item1 == "max");
but this will assign s a list of tuples, which I don't want. I just want a list of the description strings, that is, it should return a list<string> with all the descriptions associated with the Item1 string "max".
Use Select:
var s = Descriptions.Where (x => x.Item1 == "max").Select(y => y.Item2);
That will return an IEnumerable<string>. If you want a list, you need to also add ToList at the end:
var s = Descriptions.Where (x => x.Item1 == "max").Select(y => y.Item2).ToList();
Or you can use Query syntax:
var s = from d in Descriptions
where d.Item1 == "max"
select d.Item2;
That's the same as the first option. In fact, the compiler will translate query syntax to linq's extension methods.
After Where() you can use Select() method to get only description which in your case will be Item2 of Tuple and you would need to do like:
var s = Descriptions.Where(x => x.Item1 == "max")
.Select(x=>x.Item2); // projects only Description
This will return you all elements in form of IEnumerable<string> which have Item1 having value "max" and if you really want to get it as List<string>, then you can add ToList() method call at end.
Hope it helps.
if you don't use the other solutions try using a dictionary instead of a list of tuples. by the looks of your content this is more what you might want (only if your names are unique).
Dictionary<string, string> NameDesc = new Dictionary<string, string>();
NameDesc.Add("max", "desc1");
NameDesc.Add("tim", "desc2");
var description = NameDesc["max"];
var maxExists = NameDesc.ContainsKey("max");

Alter/modify data from an existing keyValuePair

I have this
foreach (KeyValuePair<string, Object> tempData in tempList.ToDictionary(x => x.Key, y => y.Value))
{
tempData["fahrzeugA"] = "s";
}
But using tempData["fahrzeugA"] = "s"; will not work.
I get:
Cannot apply indexing with [] to an expression of type
'System.Collections.Generic.KeyValuePair'
What is the correct syntax if I have an existing key fahrzeugA, which I want to alter ?
You can apply this :
var tempList = new List<Test>();
var dic = tempList.ToDictionary(x => x.Key, y => y.Value);
foreach (var tempData in dic)
{
dic[tempData.Key] = "s";
}
You can't change the key value pair since it is an immutable struct. The only way to change it is to create a new instance. That instance would live independent from the dictionary.
If you want to change the value in the dictionary, use the indexer property on the dictionary to change the value.
And even then, the dictionary will go out of scope immediately, so there is no use setting it. It won't affect the original list.
Check KeyValuePair.Value Property. It's readonly and can't be altered.
ToDictionary creates a new object. You can't alter original object by accessing its elements' value.
You have to remove this specific item from original list and add new item of the same key back.
var removeIndex = tempList.FindIndex(kp => kp.Key == "fahrzeugA");
tempList.RemoveAt(removeIndex);
tempList.Add(new KeyValuePair<string, string>("fahrzeugA", "s"));
If there are multiple "fahrzeugA" items (it's valid in list but not valid in dictionary), use RemoveAll instead.
If your tempList is List<KeyValuePair<string, Object>> type:
for (var i = 0; i < tempList.Count; ++i) {
if (tempList[i].Key == "fahrzeugA") {
tempList[i] = new KeyValuePair<string, object> ("fahrzeugA", "s"); // KeyValuePair<string, object> might be changed with your own type if you use something else.
break; // If you want to modify only first KeyValuePair.
}
}
If you have successfully turned your tempList into a dictionary, there can only be one "fahrzeugA" (since all keys must be unique), so looping makes no sense.
You should be able to just say:
var dictionary = tempList.ToDictionary(x => x.Key, y => y.Value);
dictionary["fahrzeugA"] = "s";
If you don't want to create the dictionary in the first place, you could do this:
var matchingKeyValuePair = tempList.SingleOrDefault(x => x.Key == "fahrzeugA");
if (matchingKeyValuePair != null) matchingKeyValuePair.Value = "s";
If you are using a list of .NET KeyValuePair<TKey, TValue>, which is an immutable struct, you can replace the value with a new KeyValuePair, like this:
var matchingIndex = tempList.FindIndex(x => x.Key == "fahrzeugA");
if (matchingIndex >= 0)
tempList[matchingIndex] = new KeyValuePair<string, string>("fahrzeugA", "s");
Note, this assumes that you only have one item with a key of "fahrzeugA".

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]);

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

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

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