Remove all items that match a condition in IDictionary
Asked Answered
W

2

9

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]);
Woad answered 10/5, 2014 at 22:43 Comment(4)
What is it's real type? If it's actually a generic ditionary you can simply cast it. You can try-cast it via as operator. For example: var dict = sourceAttrs as Dictionary<string, object>. It's null if the cast doesn't work.Helios
don't know, looks like a hashtable with <string, object>Woad
Please, do not include information about a language used in a question title unless it wouldn't make sense without it. Tags serve this purpose.Convolvulus
Got it thanks for future referenceWoad
H
14

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.

Helios answered 10/5, 2014 at 22:47 Comment(6)
I don't where the Where method with an IDictionary object (I've included system.linq and using .net4 client framework)Woad
@rboy: you need a generic dictionary like IDictionary<string, object> dict = new Dictionary<string, object>()Helios
I will edit the question to add more details, I don't know what the IDictionary contains. It is returned to me by an abstract interface.Woad
Thanks and will be case insensitive for thumb?Woad
Why not use: sourceAttrs = sourceAttrs.Keys.Cast<string>() .Where(key => !key.ToLower().Contains("thumb")) .ToDictionary(key => key, key => sourceAttrs[key]);Woad
Thanks, I updated the answer, let me know if that's the correct answer now.Woad
A
2

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.

Antiar answered 18/9, 2019 at 21:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.