2

The question should be clear from the title itself. I need to check if an item exist in the dictionary and remove it from the dictionary in C#. The only catch is that i have to do this using only the value item and not the key.

The declaration is as below:

IDictionary<string, myCustomClassObject> clients = new IDictionary<string, myCustomClassObject>();

Now i fill in the dictionary by:

clients["key"] = myCustomClassObject1;

Now how can i find and remove this item myCustomClassObject1 from my Dictionary. I only want to use the value item and not the key

Is this doabale...if so please guide... regards

Edit: Thank you all....got valuable comments...probably have some thinking to do ...thanks

Why
  • 626
  • 11
  • 29
  • 1
    There is a high chance that you have duplicated `values` in your dictionaries (with different `keys`), you still want to remove all? – King King Sep 20 '13 at 09:02
  • 1
    Iterate through the clients and check for equality with your value you want to find. Once found, you can save its key and use Remove(TKey) to remove it. There is no way to not use the key for removing. – Dennis Ziolkowski Sep 20 '13 at 09:03
  • 1
    You can't really not use the key. Key and value are an inseparable pair in dictionaries. You can assign null to the value, but you can't remove it entirely. Also remember about references - assigning null to a dictionary value might mess up your reference somewhere else if the value was of a reference type. – S_F Sep 20 '13 at 09:04
  • 1
    What Dennis said, and also: when you start looking for values you should think if a dictionary was the right data structure after all. – Jon Sep 20 '13 at 09:04
  • can we try using the Hashset object here. http://msdn.microsoft.com/en-us/library/bb359438.aspx. Hashset cannot have duplicates. – Karan Sep 20 '13 at 09:08
  • This similar thread might provide some guidance for you: http://stackoverflow.com/questions/2992001/how-to-delete-entries-from-a-dictionary-using-the-value – Damon J. Murray Sep 20 '13 at 09:11

5 Answers5

6

It depends on how you need it to perform. If you can accept O(N) performance, you could just do something like:

foreach(var pair in clients) {
    if(pair.Value == expected) {
        clients.Remove(pair.Key);
        break;
    }
}

However, if you need faster you would need two dictionaries - one the reverse of the other (i.e. keyed by the instances). So when adding, you would do:

clientsByKey.Add(key, value);
clientsByValue.Add(value, key);

so you can do (to remove-by-value):

string key;
if(clientsByValue.TryGetValue(value, out key)) {
    clientsByValue.Remove(value);
    clientsByKey.Remove(key);
}

or similarly (to remove-by-key):

Foo value;
if(clientsByKey.TryGetValue(key, out value)) {
    clientsByValue.Remove(value);
    clientsByKey.Remove(key);
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
4

It's not very efficient to search a dictionary by it's values. However, you can use Linq to find all entries with a given value.

IEnumerable<KeyValuePair<string, myCustomClassObject>> pairs = clients
    .Where(entry => entry.Value.Equals(myCustomClassObject1)).ToList();
foreach (KeyValuePair<string, myCustomClassObject> kv in pairs)
    clients.Remove(kv.Key);
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • 3
    You will need to convert `pairs` to a list or array, otherwise you'll get a "Collection was modified; enumeration operation may not execute" exception (because otherwise the initial `where()` will be deferred). `foreach (var kv in pairs.ToList())` would do it. – Matthew Watson Sep 20 '13 at 09:28
  • @MatthewWatson: Good point, thanks. Edited my answer accordingly. – Tim Schmelter Sep 20 '13 at 09:31
1

This should do it. It removes all clients having a given value.

while (clients.ContainsValue(myCustomClassObject1))
    clients.Remove(clients.Where(x => x.Value == myCustomClassObject1).FirstOrDefault().Key);

Or create a new dictionary without the values you want removed

clients = clients.Where(x => x.Value != myCustomClassObject1).ToDictionary(k => k.Key, v => v.Value);
Ovidiu
  • 1,407
  • 12
  • 11
  • Yes, if the list is not large enough, inefficient but **simple** solution may be acceptable. – King King Sep 20 '13 at 09:10
  • You just can't do that. You'll get an exception because you are iterating over a collection that was modified. – Matthew Watson Sep 20 '13 at 09:12
  • @Jon The whole idea of searching a dictionary by value is not very efficient. That's not what dictionaries are meant for. – Ovidiu Sep 20 '13 at 09:14
  • @MatthewWatson That's why I used while and not foreach – Ovidiu Sep 20 '13 at 09:15
  • @Ovidiu: Sure, but that's not enough justification to iterate over what could be the whole dictionary for each value you will be removing. – Jon Sep 20 '13 at 09:21
1

Use, Following will remove only first matching value

client newClient = new client();

foreach(KeyValuePair<string, client> client in clients) {
    if(client.value.equals(newClient)) {
        clients.remove(client.key);
        break;
    }       
}

Or if you want to remove all matching values,

foreach(var client in clients.Where(kvp => kvp.Value == newClient).ToList()) {
    clients.Remove(client.Key);
}
Deepak Ingole
  • 14,912
  • 10
  • 47
  • 79
1

If the collection only contains one item with the value to be removed then you can use one of the other answers here, which will work just fine.

However, if your collection can have multiple items with the same value then you need to be careful.

You cannot modify a collection while iterating over it, so you will need to find the keys of all the items that you want to remove in one loop and put them in a list, and then iterate over that list in a separate loop to delete the items.

For example:

using System;
using System.Collections.Generic;
using System.Linq;

namespace Demo
{
    class Program
    {
        void run()
        {
            var dict = new Dictionary<string, int>
            {
                {"Key1", 1}, 
                {"Key2", 2}, 
                {"Key3", 3}, 
                {"Key4", 2}, 
                {"Key5", 4}
            };

            int valueToRemove = 2;

            var keysToRemove = (from element in dict
                                where element.Value == valueToRemove
                                select element.Key).ToList();

            foreach (var key in keysToRemove)
                dict.Remove(key);

            foreach (var element in dict)
                Console.WriteLine("Key = {0}, Value = {1}", element.Key, element.Value);
        }

        static void Main(string[] args)
        {
            new Program().run();
        }
    }
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276