0

I am Looking for a cleaner way to perform the following operation:

Filter out distinct values from a dictionary of type Dictionary<T,IEnumerable<T>> , based on the uniqueness of the Value (i.e. unique by one of the attributes of T in the IEnumerable<T>).

We can ignore the key on the dictionary. Can someone suggest a good way of achieving the above ?

Casey
  • 3,307
  • 1
  • 26
  • 41
user398165
  • 44
  • 3
  • What language? Is this Java? – tobias_k Mar 13 '15 at 15:18
  • I don't think there is enough detail here to solve your problem, exactly. What do you mean "unique by one of the attributes of the T?" Something like `l.Where(l => l.Value.Any(v => v.someField == 1))`? – Casey Mar 13 '15 at 15:21
  • Yes sorry.. its c#, and unique by one of the attributes of the T i mean no two items in the dictionary should have different keys having same lists associated with them. – user398165 Mar 13 '15 at 15:29
  • I still don't follow. No two dictionary keys should be associated with the same (i.e., reference-equal) list value? Is that what you mean? – Casey Mar 13 '15 at 15:32
  • Yes that is what i meant. – user398165 Mar 13 '15 at 15:53
  • Check out these "distinct by" answers. http://stackoverflow.com/questions/2537823/distinct-by-property-of-class-by-linq You could do something like `dist.DistinctBy(d => d.Value)` – Casey Mar 13 '15 at 19:55

3 Answers3

1

Jurgen Camilleri has the piece for 'SelectMany().Distinct()'. For the comparer, we use the following comparer all the time to compare based on a property:

Usage

//this uses 'Product' for 'T'.  If you want to just use 'T' you'd have to constrain it

var distinctValues = dictionary.SelectMany((entry) => entry.Value)
.Distinct(new LambdaEqualityComparer<Product>(p=>p.ProductName));

Code

public class LambdaEqualityComparer<T> : IEqualityComparer<T>
    {
        private Func<T,object> _action;
        public LambdaEqualityComparer(Func<T, object> action)
        {
            _action = action;
        }

        public bool Equals(T x, T y)
        {
            var areEqual = baseCheck(x, y) ?? baseCheck(getObj(x), getObj(y));

            if (areEqual != null)
            {
                return areEqual.Value;
            }

            return _action(x).Equals(_action(y));
        }

        public int GetHashCode(T obj)
        {
            return _action(obj).GetHashCode();
        }

        /// <summary>
        /// True = return true
        /// False = return false
        /// null = continue evaluating
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private bool? baseCheck(object x, object y)
        {
            if (x == null && y == null)
            {
                return true;
            }
            else if (x == null || y == null)
            {
                return false;
            }

            return null;

        }

        private object getObj(T t)
        {
            try
            {
                return _action(t);
            }
            catch (NullReferenceException)
            {

            }

            return null;
        }
    }
BlackjacketMack
  • 5,472
  • 28
  • 32
0

This should work for your case:

IEnumerable<T> allValues = dictionary.SelectMany((entry) => entry.Value);
IEnumerable<T> distinctValues = allValues.Distinct(comparer);

Make sure that you create a comparer class which implements IEqualityComparer<T> so that Distinct() can differentiate between instances of T (unless T is a class which already has a comparer in the .NET Framework such as System.String).

Jurgen Camilleri
  • 3,559
  • 20
  • 45
0

I wrote a method that will merge dictionaries removing duplicates. Perhaps you could use the guts of this to perform something similar.

public static Dictionary<TKey, List<TValue>> MergeDictionaries<TKey, TValue>(this IEnumerable<Dictionary<TKey, List<TValue>>> dictionaries)
        {
            return dictionaries.SelectMany(dict => dict)
                         .ToLookup(pair => pair.Key, pair => pair.Value)
                         .ToDictionary(group => group.Key, group => group.SelectMany(value => value).Distinct().ToList());
        }

Maybe, just this section:

.ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.SelectMany(value => value).Distinct().ToList());
MattC
  • 3,984
  • 1
  • 33
  • 49