Click here to Skip to main content
15,885,818 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hey guys, I need a little help with a little problem. I have a generic list of objects which I want to filter by 2 values an only be let with the filtered list.

My Object Structure:
C#
public class ErrorLog
{
    public int ErrorLogId {get; set; }
    public string ErrorGroupId {get; set; }
    public string ErrorDescription {get; set; }
    public DateTime ErrorDate {get; set; }

}


So in my code I have my list:
C#
List<ErrorLog> _unprocessedErrors = ErrorLogBusinessLogic.GetErrors();


What I have tried :
C#
//Aggregate up the errors
List<ErrorLog> _distinctList = (List<ErrorLog>)_unprocessedErrors.Select(e => e.ErrorGroupId).Select(e => e.ErrorDescription).Distinct();

So I need to filter by ErrorGroup and ErrorSescription to get my distinct list
Does anyone know the best way to achieve this.
Thanks guys
Posted
Comments
Sergey Alexandrovich Kryukov 14-Nov-14 12:01pm    
What is the problem (performance, incorrect results, anything else...)?
—SA

Why don't you join them in one?

string someId = "someId"; 
string someDesc = "someDesc";
		
List<ErrorLog> _distinctList = _unprocessedErrors.Where(
               // do the searching...
               e => e.ErrorGroupId == someID && e.ErrorDescription == someDesc
               // get distince and convert to list
               ).Distinct().ToList();


This is a LAMBDA expression to select the objects depending on conditions. You will have to pass the still same comparison exression (variable == value) thing to select the objects.

Also, why don't you change the ErrorGroupID to an int value?
 
Share this answer
 
v4
Comments
BillWoodruff 14-Nov-14 12:39pm    
My vote of #1: Even if you had written a Linq statement that would compile (yours will not), the result of your Linq statement ... if you assigned to a 'var variable ... would have been at best a List containing two elements: the boolean values 'true and 'false.

Note that I wouldn't bother to write this to you if I didn't think you were a bright person with technical talent, and I don't like writing this kind of message, but I think you've really gotten off-track in CodeProject, probably chasing the reputation-thing, and somebody needs to tell you.

When are you going to stop posting answers which you are obviously making up quickly with no concern for accuracy, or relevance ? Posting answers on CodeProject may be some kind of video-game for you, but, for others of us here it's a place where we sincerely try to help others to the best of our abilities.
Afzaal Ahmad Zeeshan 14-Nov-14 13:20pm    
Thanks for bringing this up, you're right, I really need to put a break in this to answer when I'm above fully sure that my answer is right and would compile. I'm sorry for the trouble I've cause.

I've somehow editted the code to make it a little bit fair to send to the OP. Thanks again Bill.
There are several ways to achieve this.

You could pass a custom IEqualityComparer<T> implementation[^] to the Distinct method[^]:
C#
public sealed class ErrorLogEqualityComparer : IEqualityComparer<ErrorLog>
{
    public int GetHashCode(ErrorLog obj)
    {
        if (obj == null) return 0;
        return StringComparer.Ordinal.GetHashCode(obj.ErrorGroupId)
            ^ StringComparer.Ordinal.GetHashCode(obj.ErrorDescription);
    }
    
    public bool Equals(ErrorLog left, ErrorLog right)
    {
        if (left == null) return right == null;
        if (right == null) return false;
        
        return StringComparer.Ordinal.Equals(left.ErrorGroupId, right.ErrorGroupId)
            && StringComparer.Ordinal.Equals(left.ErrorDescription, right.ErrorDescription);
    }
}

List<ErrorLog> distinctList = _unprocessedErrors
    .Distinct(new ErrorLogEqualityComparer())
    .ToList();


You could use the GroupBy method[^] to group your results by the key values, and then extract the first item from each group:
C#
List<ErrorLog> distinctList =  _unprocessedErrors
    .GroupBy(e => new { e.ErrorGroupId, e.ErrorDescription }, (key, list) => list.First())
    .ToList();


Or, you could use a custom DistinctBy method, such as the one provided by the MoreLinq library[^]:
C#
public static class SetExtensions
{
    private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer)
    {
        var knownKeys = new HashSet<TKey>(keyComparer);
        try
        {
            foreach (var item in source)
            {
                if (knownKeys.Add(keySelector(item)))
                {
                    yield return item;
                }
            }
        }
        finally
        {
            // HACK: Avoid iterator memory leak described at:
            // http://codeblog.jonskeet.uk/2011/01/18/gotcha-around-iterator-blocks/
            knownKeys = null;
        }
    }

    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer = null)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (keySelector == null) throw new ArgumentNullException("keySelector");
        return DistinctByIterator(source, keySelector, keyComparer);
    }
}

List<ErrorLog> distinctList =  _unprocessedErrors
    .DistinctBy(e => new { e.ErrorGroupId, e.ErrorDescription })
    .ToList();
 
Share this answer
 
v4
Comments
BillWoodruff 14-Nov-14 12:43pm    
+5 You dealt with the probable intent of the OP in using 'Distinct
Maciej Los 14-Nov-14 14:29pm    
+5!
Um... Select doesn't filter - it returns a "different" object for each object in the original collection. So the first Select will result in a list of strings, which you then try to rework with the second list.
If you really want to filter then try Where:
C#
List<errorlog> _distinctList = _unprocessedErrors.Where(e => e.ErrorGroupId == 6 && e.ErrorDescription == "Hello").Distinct().ToList();</errorlog>

But if that isn't what you want, then probably we need a sample input - output set to work out what exactly you are trying to do.
 
Share this answer
 
Comments
BillWoodruff 14-Nov-14 12:46pm    
OG, I think in this case, since this is a custom class ... assuming the OP really wanted to use 'Distinct for some reason ... you'd need to implement IEquatable<t> in the ErrorLog class. Otherwise, without that, 'Distinct would just be a no-op.

MSDN: "If you want to return distinct elements from sequences of objects of some custom data type, you have to implement the IEquatable<t> generic interface in the class."
OriginalGriff 14-Nov-14 14:00pm    
To be honest Bill, I'm not sure the OP knows exactly what he wants to produce himself - the original code wouldn't even compile, so it's difficult to tell.
frostcox 14-Nov-14 15:18pm    
sorry if I was unclear with what I was trying to do do here, I tried to be as clear as I could, but the solution by Richard was exactly what I was looking for. Thanks for your help.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900