Remove Item in Dictionary based on Value - c#

I have a Dictionary<string, string>.
I need to look within that dictionary to see if a value exists based on input from somewhere else and if it exists remove it.
ContainsValue just says true/false and not the index or key of that item.
Help!
Thanks
EDIT: Just found this - what do you think?
var key = (from k in dic where string.Compare(k.Value, "two", true) ==
0 select k.Key).FirstOrDefault();
EDIT 2: I also just knocked this up which might work
foreach (KeyValuePair<string, string> kvp in myDic)
{
if (myList.Any(x => x.Id == kvp.Value))
myDic.Remove(kvp.Key);
}

Are you trying to remove a single value or all matching values?
If you are trying to remove a single value, how do you define the value you wish to remove?
The reason you don't get a key back when querying on values is because the dictionary could contain multiple keys paired with the specified value.
If you wish to remove all matching instances of the same value, you can do this:
foreach(var item in dic.Where(kvp => kvp.Value == value).ToList())
{
dic.Remove(item.Key);
}
And if you wish to remove the first matching instance, you can query to find the first item and just remove that:
var item = dic.First(kvp => kvp.Value == value);
dic.Remove(item.Key);
Note: The ToList() call is necessary to copy the values to a new collection. If the call is not made, the loop will be modifying the collection it is iterating over, causing an exception to be thrown on the next attempt to iterate after the first value is removed.

Dictionary<string, string> source
//
//functional programming - do not modify state - only create new state
Dictionary<string, string> result = source
.Where(kvp => string.Compare(kvp.Value, "two", true) != 0)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
//
// or you could modify state
List<string> keys = source
.Where(kvp => string.Compare(kvp.Value, "two", true) == 0)
.Select(kvp => kvp.Key)
.ToList();
foreach(string theKey in keys)
{
source.Remove(theKey);
}

Loop through the dictionary to find the index and then remove it.

Here is a method you can use:
public static void RemoveAllByValue<K, V>(this Dictionary<K, V> dictionary, V value)
{
foreach (var key in dictionary.Where(
kvp => EqualityComparer<V>.Default.Equals(kvp.Value, value)).
Select(x => x.Key).ToArray())
dictionary.Remove(key);
}

You can use the following as extension method
public static void RemoveByValue<T,T1>(this Dictionary<T,T1> src , T1 Value)
{
foreach (var item in src.Where(kvp => kvp.Value.Equals( Value)).ToList())
{
src.Remove(item.Key);
}
}

In my case I use this
var key=dict.FirstOrDefault(m => m.Value == s).Key;
dict.Remove(key);

Related

Find and remove elements from List<Dictionary<string, object>>

I'm currently working on a C# 4.7.2 application. I'm about to write an extension method for a custom type and I'm struggling with my LINQ query unfortunately.
I need filter a List<Dictionary<string, object>> to find the elements of Dictionary<string, object> in the list with a certain key and remove it from my list. Furthermore, a list entry can be null.
A list entry (dictionary) can look like this, there can be several elements with value key A, i need to remove all actually:
Key | Value
"MyId" : "A",
"Width" : 100,
"Length" : 50
Very simple structure. The tricky thing is to find the dictionary elements in the list. My extension method looks like that:
public static List<Dictionary<string, object>> RemoveItem(this List<Dictionary<string, object> items, string value)
{
var itemToRemove = items.FirstOrDefault(x => x.ContainsKey("MyId")).Values.Contains(value);
items.Remove(itemToRemove);
return items;
}
Unfortunately this LINQ query does not work correctly.
Do you know how to solve this issue?
Thank you very much!!
You want to remove all with a key and a value? You don't even need LINQ:
public static int RemoveItems(this List<Dictionary<string, object>> dictionaryList, string value)
{
int removed = dictionaryList
.RemoveAll(dict => dict.TryGetValue("MyId", out object val) && value.Equals(val));
return removed;
}
You could use the method RemoveAll of the list. Then you give in a predicate that checks the dictionary (which is a collection of KeyValuePair<TKey, TValue>):
items.RemoveAll(dict => dict.Any(kv => kv.Key == "MyId" && ( kv.Value as string ) == "A"));
Or as suggested by Tim Schmelter:
items.RemoveAll(dict => dict.TryGetValue("MyId", out object value) && (value as string) == "A");
You would need to do something like:
itemsToRemove = items.Where(x => x.ContainsKey("MyId") && x["MyId"].ToString() == value);
From your description, you seem to want the first dictionary containing the key with the value from the parameter value. That would be items.FirstOrDefault(x => x.ContainsKey(value))
What you are doing is getting dictionary containing one predefined key "myId" and then going through the objects inside the dictionary and comparing their values with your value parameter, which is not what you described you want.
If you expect more dictionaries to contain the given key,and you want to remove all of them, you should use list.RemoveAll(dict => dict.ContainsKey(value))
public static List<Dictionary<string, object>> RemoveItem(this List<Dictionary<string, object>> items, string value, string key)
{
foreach (var item in items)
{
if(item.ContainsKey(key) && item[key] == value)
{
item.Remove(key);
}
}
return items;
}
This gonna work just fine.

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".

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

Query list of dictionary C# Linq

I am trying to query a list of dictionary by dictionary key and value using linq. The following gives me the error of "cannot convert keyvaluepair to type bool."
Thanks in advance.
var list = new List<Dictionary<string, object>>();
foreach (DataRow row in wordCloud.Rows)
{
var dict = new Dictionary<string, object>();
foreach (DataColumn col in wordCloud.Columns)
{
dict[col.ColumnName] = row[col];
}
list.Add(dict);
}
if (!string.IsNullOrWhiteSpace(text))
{
var item = list.Where(dict => dict.Where(x => x.Key == "word" && x.Value == text)).FirstOrDefault();
}
Thanks this is what I am using.
var item = list.Where(dict => dict["WORD"].Equals(text)).FirstOrDefault();
Your compiler error is caused by your predicate in list.Where not using a Boolean expression. dict.Where(...) is going to produce an IEnumerable<KeyValuePair<K,V>>, which is not a Boolean operation. Furthermore, your technique misuses a dictionary, because it will only have one pair that has a given key, there is no need to loop over it. To deal with both issues, I suggest writing a method to investigate the dictionary and produce a Boolean result for matches.
bool DictionaryContainsText(Dictionary<string, object> dictionary, string text)
{
string key = "word";
if (dictionary.ContainsKey(key) && dictionary[key] != null)
{
return dictionary[key].Equals(text);
}
return false;
}
You can then consume this method in the filtering of your list.
var item = list.Where(dict => DictionaryContainsText(dict, text)).FirstOrDefault();
All that said, I wonder if you are starting from the wrong design? A DataTable to a List<Dictionary<K,V>> seems a bit less intuitive to use than a list of a defined type. Should you not consider defining a class with appropriately named (and typed!) properties that you could consume instead? This is left as an activity for you to consider.
Your error is happening because the predicate of list.Where( ... ) is dict => dict.Where( ... ) which isn't a Boolean value.
Depending on how you want your code to work, you could potentially replace it with list.FirstOrDefault(dict => dict.Any( ... )), which would eventually return the first dictionary that contains the key-value pair ("word", text). (I think this is the intended functionality of the code, but I can't be positive without further 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