Reputation: 6193
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
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
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
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
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