0

I want to create a Linq-Extension to distinct and filter my User objects within one method:

public static List<User> FilterDistinct<TKey>(this IEnumerable<User> source, Func<User, TKey> key)
{
    return source.Where(x => key(x) != null && key(x).ToString() != string.Empty).Select(x => key(x)).Distinct().ToList();
}

The call should look like userList.FilterDistinct(x => x.LastName)

But there is still a Syntax error within my code I can't figure out.

Error:

Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List'

5
  • 1
    what was the error saying Commented Sep 9, 2016 at 8:05
  • @un-lucky added Commented Sep 9, 2016 at 8:09
  • So, the question is, are you trying to return a distinct list of LastName only, or a distinct list of User objects by last name? Commented Sep 9, 2016 at 8:13
  • you're selecting a property from type User and than calling a Distinct and ToList on an ienumerable of those properties Commented Sep 9, 2016 at 8:14
  • 1
    You could take a look at morelinq, which already has a DistinctBy method : github.com/morelinq/MoreLINQ Commented Sep 9, 2016 at 10:20

3 Answers 3

3

What is returned from your LINQ query will be of type List<TKey> - ie, you're using a Func<User,TKey> to Select from your original list.

However, on the body of the extension method you've said it will return a List<User>.

So, then the function you pass in is pulling just the LastName from your users and returning a distinct list of them. Your return is List<string> not List<User> as you probably expect.

Sign up to request clarification or add additional context in comments.

Comments

2

IMO, @tym32167's answer breaks desired behavior, since you need List<User> as output.

You need to build up custom EqualityComparer<User> to call Distinct on User objects. Something like this:

static class MyExtensions
{
    private class UserByKeyComparer<TKey> : EqualityComparer<User>
    {
        private readonly Func<User, TKey> keySelector;
        private readonly EqualityComparer<TKey> keyComparer;

        public UserByKeyComparer(Func<User, TKey> keyFunc)
        {
            this.keySelector = keyFunc;
            this.keyComparer = EqualityComparer<TKey>.Default;
        }

        public override bool Equals(User x, User y)
        {
            return keyComparer.Equals(keySelector(x), keySelector(y));
        }

        public override int GetHashCode(User obj)
        {
            return keyComparer.GetHashCode(keySelector(obj));
        }
    }

    public static List<User> FilterDistinct<TKey>(this IEnumerable<User> source, Func<User, TKey> keySelector)
    {
        return source
            .Where(x => keySelector(x) != null && keySelector(x).ToString() != string.Empty)
            .Distinct(new UserByKeyComparer<TKey>(keySelector))
            .ToList();
    }
}

Another option is to use GroupBy (but I think, that it will be slower):

public static List<User> FilterDistinct<TKey>(this IEnumerable<User> source, Func<User, TKey> keySelector)
{
    return source
        .Where(x => keySelector(x) != null && keySelector(x).ToString() != string.Empty)
        .GroupBy(keySelector)
        .Select(g => g.First())
        .ToList();
}

5 Comments

I don't see in question any requirements regarding function return value type. Why you decided that this is desired behavior?
@tym32167: Because original function returns List<User> and it is named FilterDisctinct (the keyword is "filter"). Your function behaves differently - it returns list of anything, but it can't filter the source sequence because of projection. I suspect, that OP was happy to see code, that compiles, and that's why he voted up your answer, but he still doesn't realize, what result he will get. Your code solves one problem, and invents another one.
but he passing into function a Key (not filtering criteria), so I decided that the name of function is not matched to "what user wants". Based on this, Im assuming that he wants to get distinct (and not null) keys based on users (for example, distinct ages or lastnames). In that case my solution seems should do exactly what user wants. Just asking your opinion about it.
@tym32167: "I want to ... filter my User objects". This is citation. Maybe, OP will clarify what he wants - he will receive notifications about our comments. :)
yeach, "filter my User objects" - I understand this as "I want get filtered something from User collection". So, I think that the question can be interpreted differently. I understand your point and I would think same if function looks like Filter(this collection, func<T, bool> criteria)
-1

Try to edit return value of your method.

public static List<TKey> FilterDistinct<TKey>(this IEnumerable<User> source, Func<User, TKey> key)
{
    return source.Where(x => key(x) != null && key(x).ToString() != string.Empty).Select(x => key(x)).Distinct().ToList();
}

5 Comments

This won't return list of Users, as it stated in question.
This answer is clearly wrong, as it will return a list of keys, not User objects.
For me is not clear, why this method should return User object? I don't see any requirements regarding this. If user want to get User list as output, he able to call userList.FilterDistinct(x => x) and get what expected.
@tym32167: (x => x) - how this will filter source sequence?
@Dennis this key selector (look at parameter name). Function filters all nulls keys from original source and get distinct of them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.