Question

I'm trying to remove all elements in a IDictionary object that match a condition.

E.g. the IDictionary contains a set of keys and corresponding values (let say 80 objects). The keys are strings, the values could be of different types (think extracting metadata from a wtv file using directshow).

Some of the keys contains the text "thumb", e.g. thumbsize, startthumbdate etc. I want to remove all objects from the IDictionary who's keys contain the word thumb.

The only way I'm seeing here is to manually specify each key name using the .Remove method.

Is there some way to get all the objects who's keys contain the word thumb and them remove them from the IDictionary object.

The code looks like this:

IDictionary sourceAttrs = editor.GetAttributes();

GetAttributes is defined as:

public abstract IDictionary GetAttributes();

I don't have control over GetAttributes, it's returns an IDictionary object, I only know the contents by looking at it while debugging. (likely a HashTable)

UPDATE: Final Answer thanks to Tim:

sourceAttrs = sourceAttrs.Keys.Cast<string>()
                 .Where(key => key.IndexOf("thumb", StringComparison.CurrentCultureIgnoreCase) == -1)
                 .ToDictionary(key => key, key => sourceAttrs[key]);
Was it helpful?

Solution

So you want to remove all entries where the key contains a sub-string.

You can use LINQ by keeping all that does not contain it:

dict = dict
  .Where(kv => !kv.Key.Contains("thumb"))
  .ToDictionary(kv => kv.Key, kv => kv.Value);

If you want a case-insensitive comparison you can use IndexOf:

dict = dict
  .Where(kv => kv.Key.IndexOf("thumb", StringComparison.CurrentCultureIgnoreCase) == -1)
  .ToDictionary(kv => kv.Key, kv => kv.Value);

Update according to your non-generic edit:

If it's a non-generic dictionary like a HashTable you cannot use LINQ directly, but if you know that the key is a string you could use following query:

// sample IDictionary with an old Hashtable
System.Collections.IDictionary sourceAttrs = new System.Collections.Hashtable
{ 
    {"athumB", "foo1"},
    {"other", "foo2"}
};

Dictionary<string, object> newGenericDict = sourceAttrs.Keys.Cast<string>()
    .Where(key => !key.Contains("thumb"))
    .ToDictionary(key => key, key => sourceAttrs[key]);

But maybe it's actually a generic Dictionary, you can try-cast with the as operator:

var dict = sourceAttrs as Dictionary<string, object>;

It's null if the cast didn't work.

OTHER TIPS

If your Dictionary is read only, you will need to remove the items one by one, in which case you can also use LINQ:

dict
  .Keys
  .Where(p => p.Contains("thumb"))
  .ToList
  .ForEach(p => dict.Remove(p);

Note this works because at removal you are not looping through the dictionary anymore: you first loop entirely through the dictionary to build a list of keys to delete, then you loop through this list and re-access the dictionary to remove the keys one by one.

If your dictionary is not read only and efficiency is a concern, you are better off with Tim's suggestions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top