Filtering out values from a C# Generic Dictionary

C#GenericsDictionaryFiltering

C# Problem Overview


I have a C# dictionary, Dictionary<Guid, MyObject> that I need to be filtered based on a property of MyObject.

For example, I want to remove all records from the dictionary where MyObject.BooleanProperty = false. What is the best way of acheiving this?

C# Solutions


Solution 1 - C#

If you don't care about creating a new dictionary with the desired items and throwing away the old one, simply try:

dic = dic.Where(i => i.Value.BooleanProperty)
         .ToDictionary(i => i.Key, i => i.Value);

If you can't create a new dictionary and need to alter the old one for some reason (like when it's externally referenced and you can't update all the references:

foreach (var item in dic.Where(item => !item.Value.BooleanProperty).ToList())
    dic.Remove(item.Key);

Note that ToList is necessary here since you're modifying the underlying collection. If you change the underlying collection, the enumerator working on it to query the values will be unusable and will throw an exception in the next loop iteration. ToList caches the values before altering the dictionary at all.

Solution 2 - C#

Since Dictionary implements IEnumerable<KeyValuePair<Key, Value>>, you can just use Where:

var matches = dictionary.Where(kvp => !kvp.Value.BooleanProperty);

To recreate a new dictionary if you need it, use the ToDictionary method.

Solution 3 - C#

You can simply use the Linq where clause:

var filtered = from kvp in myDictionary
               where !kvp.Value.BooleanProperty
               select kvp

Solution 4 - C#

	public static Dictionary<TKey, TValue> Where<TKey, TValue>(this Dictionary<TKey, TValue> instance, Func<KeyValuePair<TKey, TValue>, bool> predicate)
	{
		return Enumerable.Where(instance, predicate)
                         .ToDictionary(item => item.Key, item => item.Value);
	}

Solution 5 - C#

Here is a general solution, working not only for boolean properties of the values.

Method

Reminder: Extension methods must be placed in static classes. Dont forget the using System.Linq; statement at the top of the source file.

    /// <summary>
    /// Creates a filtered copy of this dictionary, using the given predicate.
    /// </summary>
    public static Dictionary<K, V> Filter<K, V>(this Dictionary<K, V> dict,
            Predicate<KeyValuePair<K, V>> pred) {
        return dict.Where(it => pred(it)).ToDictionary(it => it.Key, it => it.Value);
    }

Usage

Example:

    var onlyWithPositiveValues = allNumbers.Filter(it => it.Value > 0);

Solution 6 - C#

I added the following extension method for my project which allows you to filter an IDictionary.

public static IDictionary<keyType, valType> KeepWhen<keyType, valType>(
    this IDictionary<keyType, valType> dict,
    Predicate<valType> predicate
) {
    return dict.Aggregate(
        new Dictionary<keyType, valType>(),
        (result, keyValPair) =>
        {
            var key = keyValPair.Key;
            var val = keyValPair.Value;

            if (predicate(val))
                result.Add(key, val);

            return result;
        }
    );
}

Usage:

IDictionary<int, Person> oldPeople = personIdToPerson.KeepWhen(p => p.age > 29);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFiona - myaccessible.websiteView Question on Stackoverflow
Solution 1 - C#mmxView Answer on Stackoverflow
Solution 2 - C#LeeView Answer on Stackoverflow
Solution 3 - C#OdedView Answer on Stackoverflow
Solution 4 - C#Ryan WilliamsView Answer on Stackoverflow
Solution 5 - C#AndiView Answer on Stackoverflow
Solution 6 - C#aaaaaaView Answer on Stackoverflow