Chris Headleand
Chris Headleand

Reputation: 6193

Check if a c# list of objects contains an object with the same variable.

I have a class which contains three values,

public class hitOBject {
    public string v1;
    public float v2;
    public float v3;
}

I dynamically create these objects during runtime which get stored in their own list.

 List<hitOBject> Detected = new List<hitOBject>();

However, when a new object is added to this list, I first need to check if if there is already a list which contains a a duplicate value. For example...

if I have 2 hitObjects

 obj1            obj2 
 v1 = bob        v1 = bob 
 v2 = 1f         v2 = 3f     
 v3 = 2.5f       v3 = 3.5f  

and if I want to check wether V1 already exists in the list is there a method to do this?

I know that if it was a duplicated object I could just do the following code?

if(!Detected.Contains(object))

but as Im looking for a sub value this wont work?

Can anyone point me in the right direction?

Upvotes: 0

Views: 384

Answers (4)

Habib
Habib

Reputation: 223217

Override GetHashCode and Equals.If you are going to compare against only a single field/property in your class then specify that field only like:

public class hitOBject
{
    public string v1;
    public float v2;
    public float v3;

    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 ((hitOBject)obj).v1 == this.v1;
    }

    public override int GetHashCode()
    {
        return (v1 != null ? v1.GetHashCode() : 0);
    }
}

and then you can use:

List<hitOBject> Detected = new List<hitOBject>();
Detected.Add(new hitOBject {v1 = "bob", v2 = 1f, v3 = 2.5f});

hitOBject secondObject = new hitOBject {v1 = "bob", v2 = 1f, v3 = 2.5f};
if (Detected.Contains(secondObject))
{
    Console.WriteLine("Alread Exists");
}

Since List<T>.Contains uses Equals to compare for equality, the override Equals method will return a bool based on comparison of field v1.

If you are going to have unique values in your List then it is better to use HashSet<T> since it will only allow unique values based on GetHashCode and Equals implementation.

HashSet<hitOBject> Detected = new HashSet<hitOBject>();
Detected.Add(new hitOBject {v1 = "bob", v2 = 1f, v3 = 2.5f});
hitOBject secondObject = new hitOBject {v1 = "bob", v2 = 1f, v3 = 2.5f};
Detected.Add(secondObject);

In the above code, at the end, your HashSet will only contain a single item and secondObject will not be added to the HashSet.

If you don't want to override GetHashCode and Equals then you can use a LINQ query to determine if an object exists in your list like:

hitOBject secondObject = new hitOBject {v1 = "bob", v2 = 1f, v3 = 2.5f};
if (Detected.Any(r => r.v1 == secondObject.v1))
{
    //Already exists
}

Another option is to leave your class as it is and implement IEqualityComparer<T> like

private sealed class hitObjectV1EqualityComparer : IEqualityComparer<hitOBject>
{
    public bool Equals(hitOBject x, hitOBject 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 string.Equals(x.v1, y.v1);
    }

    public int GetHashCode(hitOBject obj)
    {
        return (obj.v1 != null ? obj.v1.GetHashCode() : 0);
    }
}

and then you can pass that in your HashSet<T> constructor like:

Also see: General Naming Conventions C# - MSDN

HashSet<hitOBject> Detected = new HashSet<hitOBject>(new hitObjectV1EqualityComparer());

Later adding items

Detected.Add(new hitOBject {v1 = "bob", v2 = 1f, v3 = 2.5f});
hitOBject secondObject = new hitOBject {v1 = "bob", v2 = 1f, v3 = 2.5f};
Detected.Add(secondObject);

You will end up with a single item in the HashSet.

You can use that Comparer with List<T>.Contains as well like:

List<hitOBject> Detected = new List<hitOBject>();
var MyEqualityComparer = new hitObjectV1EqualityComparer();
Detected.Add(new hitOBject {v1 = "bob", v2 = 1f, v3 = 2.5f});

hitOBject secondObject = new hitOBject {v1 = "bob", v2 = 1f, v3 = 2.5f};
if (Detected.Contains(secondObject, MyEqualityComparer))
{
    //Already Exists
}
else
{
    Detected.Add(secondObject);    
}

Upvotes: 1

Edmund Schweppe
Edmund Schweppe

Reputation: 5132

Implement the IEquatable<T> interface in your HitObject:

public class HitObject : IEquatable<HitObject>
{
    public string V1;
    public float V2;
    public float V3;

    public bool Equals(HitObject other)
    {
        return this.V1 == other.V1;
    }
}

Then you can use the Contains method thusly:

List<HitObject> detected = new List<HitObject>();
detected.Add(new HitObject
{
    V1 = "bob",
    V2 = 1f,
    V3 = 2.5f
});
HitObject something = new HitObject
{
    V1 = "bob",
    V2 = 3f,
    V3 = 3.5f
};
if (!detected.Contains(something))
{
    // this line will not be executed
    detected.Add(something);
}

Upvotes: 0

Stilgar
Stilgar

Reputation: 23551

if(!Detected.Any(o1 => o1.v1 == o2.v1 && o1.v2 == o2.v2 && o1.v3 == o2v3))

Please follow C# conventions. Variables (Detected) should be camelCase, classes (hitObject) should be PascalCase

If the list is big (more than 50 elements) consider using HashSet as @Servy suggested

Upvotes: 1

Brian Mains
Brian Mains

Reputation: 50728

The any function is LINQ and looks for any match; if not any match, then add to the list... is that what you need?

if (!Detected.Any(Function(i) i.V1 = obj2.V1))
    Detected.Add(obj2);

Just add to get LINQ support:

using System.Linq;

Upvotes: 0

Related Questions