I have a list of items that contains duplicates, and I want to keep the last item of each group of duplicates. The native DistinctBy LINQ operator keeps the first item from each group, so it doesn't solve my problem. I tried to implement a custom DistinctLastBy operator like this:
public static IEnumerable<TSource> DistinctLastBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer = default)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(keySelector);
return source
.GroupBy(keySelector, comparer)
.Select(g => g.Last());
}
...but it's not working correctly. It does return the last item of each group, but the items are not in the same order as in the source sequence. Here is a minimal demonstration of the desirable behavior:
string[] source = ["giraffe", "Elephant", "Cat", "Eagle", "Gazelle", "Cow", "chicken"];
IEnumerable<string> distinct = source
.DistinctLastBy(x => x.Substring(0, 1), StringComparer.OrdinalIgnoreCase);
Console.WriteLine(String.Join(", ", distinct));
The animals are considered duplicates if they start with the same letter. The desirable output is:
Eagle, Gazelle, chicken
The actual output of my flawed implementation is:
Gazelle, Eagle, chicken
This output is incorrect because the "Eagle" is placed before the "Gazelle" in the input, so it should also be placed before the "Gazelle" in the output.
How can I fix my DistinctLastBy operator so that it produces the correct output?
I searched for a duplicate and I didn't find any.