Search code examples
linq.net-framework-version

How do I filter a list of objects with linq with multiple criteria?


I am trying to filter a list of objects by multiple criteria. First of all, if the Name and Code are the same for two or more records, I only want to return one. However, if the Status of one is "suspended" and the Status of the second is "rented", I only want to return the record with "suspended" status. This is what I've got so far:

public class Customer
{
    public string UnitName { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
    public string Phone { get; set; }
    public string Status { get; set; }
}

public static IEnumerable<Customer> FilterDuplicates(IEnumerable<Customer> customers)
{
    return customers
            .GroupBy(c => new {c.Name, c.Code})
            .Select(g => g.First());
}

For example, the following data should return Mister Twister, Frank Furter and the record where the status is "suspended" for Jane Jones:

{
    "unitName": "A22",
    "code": "00122",
    "status": "rented",
    "phone": "2125551212",
    "name": "Jones, Jane"
},
{
    "unitName": "A07",
    "code": "00122",
    "status": "suspended",
    "phone": "2125551212",
    "name": "Jones, Jane"
},
{
    "unitName": "C19",
    "code": "00222",
    "status": "suspended",
    "phone": "2125557777",
    "name": "Furter, Frank"
},
{
    "unitName": "B14",
    "code": "00333",
    "status": "rented",
    "phone": "2125559999",
    "name": "Twister, Mister"
}

Solution

  • You should order the contents of each group and then you can select which one you want:

    return customers
                .GroupBy(c => new {c.Name, c.Code})
                .Select(g => g.OrderByDescending(c => c.status).First());
    

    NOTE: This is essentially a particular implementation of DistinctBy, which in full general form I implement with:

    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector, Func<IGrouping<TKey, T>, T> pickFn, IEqualityComparer<TKey> comparer = null) =>
        src.GroupBy(keySelector, comparer).Select(pickFn);
    

    Which you could then use as

    return customers.DistinctBy(c => new { c.Name, c.Code }),
                                g => g.OrderByDescending(c => c.status).First());