2

I have List<Moves> listOfMoves

 ListOfMoves.Add(new Moves()
                        {
                           int position1= number1,
                            int position2= number2,
                        });

Now I want to check if ListOfMoves contains for example Move(2,3), but also to check if it contains Move(3,2). I tried if(ListOfMoves.Contains(new Move(2,3))) but this does not work properly.

  • 5
    Do you want to make this `ListOfMoves.Contains(new Move(2, 3))` work only for type `Moves` or for any type `T`? Why don't you use LINQ: `ListOfMoves.Any(m => (m.position1 == n1 && m.position2. == n2) || (m.position1 == n2 && m.position2. == n1))`? – Iliar Turdushev May 03 '20 at 22:28
  • 4
    You need to override [`Object.Equals()`](https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=netcore-3.1) and/or implement [`IEquatable`](https://learn.microsoft.com/en-us/dotnet/api/system.iequatable-1?view=netcore-3.1) interface on the `Move` class. – orhtej2 May 03 '20 at 22:29
  • `ListMoves.Any(move => move.position1 == number1 && move.position2 == number2)` will return `true` if condition is `true` at least for one item. Use Linq. – aepot May 03 '20 at 22:31
  • @IliarTurdushev I will have 8 moves in one if condition to check, so it will basicily make 16 conditions? – Raskoljnikovic May 03 '20 at 22:42
  • @aepot I need true if both positions are true. `If (list.contains(move 3,2) || list.contains(move2,3)) { true statement}` – Raskoljnikovic May 03 '20 at 22:50
  • @orhtej2 How to do that? – Raskoljnikovic May 03 '20 at 22:53
  • You may use for example construction `List.Any(...) && List.Any(..)`. And learn more about **Linq**, there are many useful extensions. – aepot May 03 '20 at 23:00
  • 2
    As @orhtej2 pointed out, you should override [`Object.Equals`](https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=netcore-3.1). Go to this link and see an example how `Object.Equals` is implemented in `Point` class. Your implementation should be the same but also it must take into account that order of the properties does not matter. – Iliar Turdushev May 03 '20 at 23:01
  • 1
    You must override **BOTH** `Equals` and `GetHashCode`. – Enigmativity May 04 '20 at 00:52

3 Answers3

2

Method List<T>.Contains(T item) internally uses method Object.Equals to check if objects are equal. Therefore if you want to use method List<T>.Contains(T item) with your type T to check if the specified item is contained in the List<T> then you need to override method Object.Equals in your type T.

When you override Object.Equals you should also override Object.GetHashCode. Here is a good explanation "Why is it important to override GetHashCode when Equals method is overridden?".

Here is how you should override Object.Equals in the Move class to fit your requirement:

class Move
{
    public Move(int p1, int p2)
    {
        position1 = p1;
        position2 = p2;
    }

    public int position1 { get; }
    public int position2 { get; }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        if (ReferenceEquals(this, obj))
            return true;

        Move other = obj as Move;

        if (other == null)
            return false;

        // Here we specify how to compare two Moves. Here we implement your
        // requirement that two moves are considered equal regardless of the
        // order of the properties.
        return (position1 == other.position1 && position2 == other.position2) ||
               (position1 == other.position2 && position2 == other.position1);
    }

    public override int GetHashCode()
    {
        // When implementing GetHashCode we have to follow the next rules:
        // 1. If two objects are equal then their hash codes must be equal too.
        // 2. Hash code must not change during the lifetime of the object.
        //    Therefore Move must be immutable. (Thanks to  Enigmativity's usefull tip).
        return position1 + position2;
    }
}

When you override Object.Equals you will be able to use condition ListOfMoves.Contains(new Move(2, 3)) to check if moves Move(2, 3) or Move(3, 2) are contained in the ListOfMoves.

Here is complete sample that demostrates overriding of Object.Equals.

Iliar Turdushev
  • 4,935
  • 1
  • 10
  • 23
  • 2
    The second rule with hash codes is that they cannot change over the lifetime of the object. `Move` must be immutable. – Enigmativity May 04 '20 at 11:59
  • 1
    Your implementation will throw an exception if the passed object is not a `Move` instance. consider using casting with `as` operator. – orhtej2 May 04 '20 at 15:45
  • @Enigmativity Thank you very much for your useful tip. I added it to the post. Also I made class `Move` immutable. – Iliar Turdushev May 04 '20 at 16:26
  • @orhtej2 Thank you for remark. I didn't want to complicate the sample therefore didn't include type checking and checking for reference equality. After your comment I decided to include these checks. – Iliar Turdushev May 04 '20 at 16:28
  • @IliarTurdushev my concern stemmed from the remark on linked document on `Object.Equals()` that the function is expected to always successfully return a value (and hence never throw). Furthermore, an interesting point they're making is one should do exact type matching if the class is not `sealed` as comparison between base and derived class may yield unwanted results. – orhtej2 May 04 '20 at 19:42
  • 1
    @orhtej2 Really interesting point about exact type matching if the class is not `sealed`. I have never thought about possibility of comparing base and derivded classes. – Iliar Turdushev May 05 '20 at 02:38
1

For this you can use LINQ's Any function. If you want both combinations for the positions [ (2,3) or (3,2) ] you'll need two pass in two checks

ListOfMoves.Any(x => 
    (x.position1 == 2 && x.position2 == 3) 
    || (x.position1 == 3 && x.position2 == 2) )

Any returns a bool so you can wrap this line of code in an if statement or store the result for multiple uses

Potential improvement

If you're going to be doing a lot of these checks (and you're using at least c# version 7) you could consider some minor refactoring and use the built in tuples type: https://learn.microsoft.com/en-us/dotnet/csharp/tuples

Moves would become

public class Moves
{
    public (int position1, int position2) positions { get; set; }
}

And the Any call would become

ListOfMoves.Any(x => x.positions == (2,3) || x.positions == (3,2))

Else where in the code you can still access the underlying value of each position as so:

ListOfMoves[0].positions.position1

Obviously depends on what else is going on in your code so totally up to you!

Sam
  • 394
  • 2
  • 4
  • 9
0

Obviously it won't work cause you can't compare the entity itself rather you will have to compare with property values like below using System.Linq

ListOfMoves.Where(x => x.position1 == 2 && x.position1 == 3)

Note: Your posted code shouldn't compile at all in first place

You said .. I need to get true if either Move(3,2) or (2,3) is in List

Then use Any() using the same predicate like

if(ListOfMoves.Any(x => x.position1 == 2 && x.position1 == 3))
{
  // done something here
}
Rahul
  • 76,197
  • 13
  • 71
  • 125