peter
peter

Reputation: 13491

Removing Duplicates in Dictionary

If I have a dictionary like so,

Dictionary<int, string> roadNames = new Dictionary<int, string>();

roadNames.Add(1, "Rosedale Rd");
roadNames.Add(2, "Transmere Rd");
roadNames.Add(3, "Rosedale Rd");
roadNames.Add(4, "Rosedale Rd");
roadNames.Add(5, "Rosedale Rd");
roadNames.Add(6, "Rosedale Rd");
roadNames.Add(7, "Rosedale Rd");
roadNames.Add(8, "Brown Rd");
roadNames.Add(9, "Harold Rd");

Is there a LINQ solution to remove the duplicates that are NEXT to each other. The result I am after is a list containing this,

Rosedale Rd
Transmere Rd
Rosedale Rd
Brown Rd
Harold Rd

Note that Rosedale Rd is still in the list twice. The idea is to remove duplicates that are next to each other, and in this case we are removing item 4, 5, 6, and 7.

Items 1 is not next to item 3, so it isn't removed.

UPDATE:

Don't worry about Dictionary not being ordered. Solutions for a list that is in order would be fine. I can handle the ordering. i.e.

List<string> roadNames = new List<string>()
{
    "Rosedale Rd",
    "Transmere Rd",
    // etc
};

Upvotes: 4

Views: 2222

Answers (2)

Enigmativity
Enigmativity

Reputation: 117064

Here's a method that uses the standard built-in LINQ operators:

var result =
    roadNames
        .OrderBy(x => x.Key)
        .Select(x => x.Value)
        .Aggregate(
            new List<string>(),
            (xs, x) =>
            {
                if (xs.LastOrDefault() != x)
                {
                    xs.Add(x);
                }
                return xs;
            });

I've assumed that you want to order by key before selecting the value from the dictionary.

Upvotes: 2

yamen
yamen

Reputation: 15618

Assuming you're using a sorted dictionary instead (or any other sorted structure), there are two options.

Leverage Reactive Extensions

This is very simple if you leverage Reactive Extensions from Microsoft (which everyone should!):

roadNames.Values // remove if a list instead
         .ToObservable()
         .DistinctUntilChanged()
         .ToList();

You can change that final ToList() to to ToEnumerable() instead if you like.

This returns:

Rosedale Rd 
Transmere Rd 
Rosedale Rd 
Brown Rd 
Harold Rd 

Use an Extension Method

You can use a GroupAdjacent extension method as such:

roadNames.Values // remove if a list instead
         .GroupAdjacent((x,y) => x == y)
         .Select(x => x.First());

The extension method:

public static IEnumerable<IEnumerable<T>> GroupAdjacent<T>(
    this IEnumerable<T> source, Func<T, T, bool> adjacent)
{
    var g = new List<T>();
    foreach (var x in source)
    {
        if (g.Count != 0 && !adjacent(g.Last(), x))
        {
            yield return g;
            g = new List<T>();
        }
        g.Add(x);
    }
    yield return g;
}

Upvotes: 2

Related Questions