1

I want to check if Object with given values exists in SortedSet<> but I don't understand how custom comparation works here. In List<>.Exists() i could just use lambda, but I cannot do that there and i don't get that whole interface thing while msdn says i need to override int returning function.

public class Node
{
    public int X, Y;
    public int rand;

    public Node(int x, int y, int r)
    { X = x; Y = y; rand = r; }
}

class Program
{
    static void Main(string[] args)
    {
        SortedSet<Node> mySet = new SortedSet<Node>();
        mySet.Add(new Node(1, 2, 90));
        Node myNode = new Node(1, 2, 50);
        // I want this to check if X and Y are the same
        if (mySet.Contains(myNode, interfaceThing))
            Console.WriteLine("Sth is already on that (X, Y) position");      
    }
}

Is there any simple way to do that?

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
ddl
  • 329
  • 6
  • 15
  • 1
    Any reason you don't want to use a [Dictionary<>](http://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx)? – Erik Philips Mar 27 '14 at 18:40
  • It's just simplified example. In my program I need some functionality that `SortedSet<>` provides. – ddl Mar 27 '14 at 18:41
  • 1
    From looking at the docs I cannot determine what is used for comparison with `SortedSet`, because of that it's difficult to say how to make this work. I would guess you need to override the `Node` classes `Equals` method so that it does some kind of custom comparison rather than the standard reference comparison that it inherits from `object`. – evanmcdonnal Mar 27 '14 at 18:47

2 Answers2

7

You have two options, create a class that implements IComparer<Node> (you should do IEqualityComparer<Node> too) and pass that in to the constructor of the sorted set.

public class NodeComparer : IComparer<Node>, IEqualityComparer<Node>
{
    public int Compare(Node node1, Node node2)
    {
        //Sorts by X then by Y

        //perform the X comparison
        var result = node1.X.CompareTo(node2.X);
        if (result != 0)
            return result;

        //Perform the Y Comparison
        return node1.Y.CompareTo(node2.Y);
    }

    public bool Equals(Node x, Node y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (ReferenceEquals(x, null)) return false;
        if (ReferenceEquals(y, null)) return false;
        if (x.GetType() != y.GetType()) return false;
        return x.X == y.X && x.Y == y.Y && x.rand == y.rand;
    }

    public int GetHashCode(Node obj)
    {
        unchecked
        {
            var hashCode = obj.X;
            hashCode = (hashCode * 397) ^ obj.Y;
            hashCode = (hashCode * 397) ^ obj.rand;
            return hashCode;
        }
    }
}

public class Node
{
    public int X, Y;
    public int rand;

    public Node(int x, int y, int r)
    { X = x; Y = y; rand = r; }
}

class Program
{
    static void Main(string[] args)
    {
        SortedSet<Node> mySet = new SortedSet<Node>(new NodeComparer());
        mySet.Add(new Node(1, 2, 90));
        Node myNode = new Node(1, 2, 50);
        // I want this to check if X and Y are the same
        if (mySet.Contains(myNode, interfaceThing))
            Console.WriteLine("Sth is already on that (X, Y) position");      
    }
}

Or have Node implement the relevant methods it needs itself.

public class Node : IEquatable<Node>, IComparable<Node>
{
    public int X, Y;

    public int rand;

    public Node(int x, int y, int r)
    { X = x; Y = y; rand = r; }

    public bool Equals(Node other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return X == other.X && Y == other.Y && rand == other.rand;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Node)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            var hashCode = X;
            hashCode = (hashCode*397) ^ Y;
            hashCode = (hashCode*397) ^ rand;
            return hashCode;
        }
    }

    public int CompareTo(Node other)
    {
        //First order by X then order by Y then order by rand

        var result = X.CompareTo(other.X);
        if (result != 0)
            return result;

        result = Y.CompareTo(other.Y);
        if (result != 0)
            return result;

        return rand.CompareTo(other.rand);
    }
}
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • This Node implementation is what i was looking for, thank you. – ddl Mar 27 '14 at 18:59
  • 1
    @user2843974 One thing you must be careful of, your `Node` must stay "frozen" while in the set, that means you can not make any changes that would effect the output of `.Compare(Node)`, if you do change `Node` while it is in the collection you may get odd behavior like duplicate objects in your `Set` or items showing up out of order when you enumerate it. – Scott Chamberlain Mar 27 '14 at 19:09
  • Now i wonder, what if i want to have equality comparation by X and Y, and sorting by rand? In Visual Studio debugger `SortedSet.Contains()` uses only `CompareTo(Node other)` and sets them equal when rand is the same... – ddl Mar 27 '14 at 19:44
  • That is easy, You pass in a custom comparer to `Sortedset` that first sorts by rand then sorts by X and Y but you implement `.Equals()` and `.GethashCode()` on `Node` comparing only X and Y. Note that this will make your SortedSet behave as a SortedList however, you will be able to add in nodes that have the same X and Y if they have different rand values. – Scott Chamberlain Mar 27 '14 at 19:49
  • 2
    It's not working for me SortedSet seems to ignore `Equals()` function, and only uses `Compare()` – ddl Mar 28 '14 at 11:19
0

A simple dirty way to do this is with some linq

if(myNode.Where(n => n.X == myNode.X && n.Y == myNode.Y).Count > 0)

You could also do this in an extension method to be able to call this more than once

public static class Extensions
{
    public static bool ContainsNode(this IList<Node> nodes, Node value)
    {
        return nodes.Where(n => n.X == value.X && n.Y == value.Y).Count > 0;
    }
}

Although if you want to be more efficient, you should use a simple foreach loop instead, to possibly shortcut iterating through the whole list once one is found.

Edit: totally forgot about .Any() bassically does the Where for you but does cut out early.

public static class Extensions
{
    public static bool ContainsNode(this IList<Node> nodes, Node value)
    {
        return nodes.Any(n => n.X == value.X && n.Y == value.Y).Count > 0;
    }
}
BenVlodgi
  • 2,135
  • 1
  • 13
  • 26
  • @soon `Count` is a property, `Count` is an extension method. And yes, that would work the same as `Any()` I believe. – BenVlodgi Mar 27 '14 at 18:56