Klyner
Klyner

Reputation: 107

Distinct values list

I am trying to get Locations out of a List locations. All of the values has to be unique from eachother in sequence.

I ask myself what I can do best.

Location contains a Latitude and a longitude.

List<Location> locations;

Location a,b,c,d;
a = locations[....]
b = locations[....]
c = locations[....]
d = locations[....]

So how should I give a,b,c and d all unique values, so that there's no Location equal to each other?

Upvotes: 0

Views: 140

Answers (3)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236188

If you don't want or can't change implementation of your Location class, and you need to get distinct locations, you can group them by latitude and longitude and then select first location from each group (all locations in group will have same latitude and longitude):

var distinctLocations = from l in locations
                        group l by new { l.Latitude, l.Longitude } into g
                        select g.First();

But of course, if that is not single place where you need to compare locations, you should go with @Tim Schmelter solution.

UPDATE: Getting unique random locations:

Random rnd = new Random();

List<Location> uniqueRandomLocations =
      locations.GroupBy(l => new { l.Latitude, l.Longitude })
               .Select(g => g.First())
               .OrderBy(l => rnd.Next())
               .Take(N)
               .ToList();

Upvotes: 0

Frode
Frode

Reputation: 3455

Use the Union method on array instances to merge distinct items (remember to include using System.Linq to get access to Linq specific extension methods):

using System;
using System.Linq;

namespace DistinctValues
{
    public struct Location
    {
        public Location(double longitude, double latitude) : this()
        {
            this.Longitude = longitude;
            this.Latitude = latitude;
        }
        public double Longitude { get; set; }
        public double Latitude { get; set; }

        public override string ToString()
        {
            return string.Format("Longitude: {0}, Latitude={1}", this.Longitude, this.Latitude);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var a = new Location[] 
            { 
                new Location(123.456, 456.789),
                new Location(123.456, 456.789),
                new Location(234.567, 890.123),
            };
            var b = new Location[]
            {
                new Location(123.456, 456.789),
                new Location(890.123, 456.789),
            };

            // Join array a and b. Uses union to pick distinct items from joined arrays
            var result = a.Union(b);

            // Dump result to console
            foreach(var item in result)
                Console.WriteLine(item);
        }
    }
}

Upvotes: 0

Tim Schmelter
Tim Schmelter

Reputation: 460028

You should override Equals and GetHashCode in your class, for example:

public class Location
{
    public int Latitude { get; set; }
    public int Longitude { get; set; }

    public override bool Equals(object obj)
    {
        if(obj == null)return false;
        if(object.ReferenceEquals(this, obj)) return true;
        Location l2 = obj as Location;
        if(l2 == null) return false;
        return Latitude == l2.Latitude && Longitude == l2.Longitude;
    }

    public override int GetHashCode()
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = 17;
            hash = hash * 23 + Latitude.GetHashCode();
            hash = hash * 23 + Longitude.GetHashCode();
            return hash;
        }
    }

    public override string ToString()
    {
        return string.Format("{0},{1}", Latitude, Longitude);
    }
}

Now you can already use Enumerable.Distinct to remove duplicates:

var uniqeLocations = locations.Distinct().ToList();

Upvotes: 1

Related Questions