Compare properties in request if they have equal value C# - c#

I want to compare if the properties in the request have the same value. I have the following json that I use for request via PostMan.
“request”:[ {
“Id”: “1234567”,
“Position”: “1”,
“IsSet”: true
},
{
“Id”: “1234587”,
“Position”: “1”,
“IsSet”: true
},
]
In the code I want to compare if properties Position and IsSet has the same value foreach id in the request. If they don’t to throw error.
public class Info
{
public string Id {get; set;}
public string Position {get; set;}
public bool IsSet {get; set;}
}
I have method called Validate to validate those properties.
public class Validate(Info context)
{
foreach (var item in context)
{
// what code should check this
}
}

You could use LINQs Select and Distinct for that purpose.
Here's a sample "Validate" method.
List<Test> objs = new List<Test>()
{
new Test(){ Position = "random position 1", IsSet = true, Id = 123 },
new Test(){ Position = "random position 2", IsSet = true, Id = 123 },
new Test(){ Position = "random position 3", IsSet = true, Id = 123 }
};
if(objs.Count() > 1){
var query = objs.Select(p => new { p.Id, p.IsSet }).Distinct();
var allTheSame = query.Count() == 1;
Console.WriteLine(allTheSame);
}else{
Console.WriteLine("Nothing To Compare Against");
}
}
The logic here is to check if there's more than 1 item in the list - just so we know there's something to compare the values against.
If more than one, select the properties you want to match objects on and call distinct on it.
Then we get the count of the distinct values, if they all match we will always get 1 returned from the query.Count() hence the boolean check.
At this point if allTheSame is false you can throw your error instead of the Console.WriteLine
In the second Console.WriteLine you can always return true since there's nothing to compare making it distinct enough.
Here's a sample dotNetFiddle.

I like Adriani6's answer. But it works only with simple classes. Best solution i think is Equals method. You can easy generate it with Resharper (Alt+insert, Equaliti members):
public class Info
{
protected bool Equals(Info other)
{
return string.Equals(Id, other.Id) && string.Equals(Position, other.Position) && IsSet == other.IsSet;
}
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((Info) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (Id != null ? Id.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (Position != null ? Position.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ IsSet.GetHashCode();
return hashCode;
}
}
public string Id { get; set; }
public string Position { get; set; }
public bool IsSet { get; set; }
}

You could simply step through your list in your Validation method with a nested loop.
Assuming it's an IEnumerable like an array or List you could do the following:
// not a class, context is IEnumerable, not a single entitry
// Returns true if OK, false if any element is not the same.
// 'Sameness' (equality) defined in Info-class as implemented by IEquatable
public bool Validate(IEnumerable<Info> context)
{
for (int i = 0; i < context.Count(); i++)
{
for (int j = i + 1; j < context.Count(); j++)
{
if (!context[i].Equals(context[j])) {return false;}
}
}
return true;
}
And Info with IEquatable
public class Info : IEquatable<Info>
{
protected bool Equals(Info other)
{
return string.Equals(Id, other.Id) && string.Equals(Position, other.Position) && IsSet == other.IsSet;
}
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((Info) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (Id != null ? Id.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (Position != null ? Position.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ IsSet.GetHashCode();
return hashCode;
}
}
public string Id { get; set; }
public string Position { get; set; }
public bool IsSet { get; set; }
}
If you want to be fancy you can overload == and != operators like so:
public static bool operator ==(Info lhs, Info rhs) { return lhs.Equals(rhs); }
public static bool operator !=(Info lhs, Info rhs) { return !(lhs == rhs); }

Related

How to find not matching records in Generic List in C# [duplicate]

This question already has answers here:
Using Linq Except not Working as I Thought
(5 answers)
Closed 4 years ago.
I have two Generic List object, I want to get records which are not matching in second Generic list object. Below is my code. But it returning all records.
I want to ignore matching record in first list.
public class CuratedIncludeUid
{
public string type { get; set; }
public string uid { get; set; }
}
List<CuratedIncludeUid> newUids = new List<CuratedIncludeUid>();
newUids.Add(new CuratedIncludeUid { type = "series", uid = "600" });
List<CuratedIncludeUid> liExistingUids = new List<CuratedIncludeUid>();
liExistingUids.Add(new CuratedIncludeUid { type = "series", uid = "600" });
liExistingUids.Add(new CuratedIncludeUid { type = "series", uid = "200" });
var ied = liExistingUids.Except(newUids).ToList(); ;
foreach (var row in ied)
{
Console.WriteLine("Uid:" + row.uid + "type:" + row.type);
}
Console.Read();
I am getting Output as below
Uid:600type:series
Uid:200type:series
**My expected output as below
Uid:200type:series**
Either override Equals and GetHashCode
public class CuratedIncludeUid
{
public string type { get; set; }
public string uid { get; set; }
protected bool Equals(CuratedIncludeUid other)
{
return string.Equals(type, other.type) && string.Equals(uid, other.uid);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals((CuratedIncludeUid) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((type != null ? type.GetHashCode() : 0) * 397) ^ (uid != null ? uid.GetHashCode() : 0);
}
}
}
Or passing an IEqualityComparer to Except
public class CuratedIncludeUidEqualityComparer : IEqualityComparer<CuratedIncludeUid>
{
public bool Equals(CuratedIncludeUid x, CuratedIncludeUid y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) return false;
return string.Equals(x.type, y.type) && string.Equals(x.uid, y.uid);
}
public int GetHashCode(CuratedIncludeUid obj)
{
unchecked
{
return ((obj.type != null ? obj.type.GetHashCode() : 0) * 397) ^ (obj.uid != null ? obj.uid.GetHashCode() : 0);
}
}
}
var ied = liExistingUids.Except(newUids, new CuratedIncludeUidEqualityComparer()).ToList();
Either, you can implement Equals and GetHashCode or IEqualityComparer, or you can also do the following:
With All:
var ied = liExistingUids.Except(newUids).ToList();
liExistingUids
.Where(x => newUids.All(y => y.type != x.type && y.series != x.series))
.ToList();
With Any:
liExistingUids
.Where(x => !newUids.Any(y => y.type == x.type && y.series == x.series))
.ToList();

Trying to set up a CompareLists method with an overridden Equals/Gethash code methods

I'm trying to set up a way to compare some nested lists with objects that I'm importing from MongoDB. I have already set up the lists object:
public class SecurityGroup
{
public ObjectId Id { get; set; }
public string GroupID { get; set; }
public string GroupName{ get; set; }
public List<IpPermission> IpPermissions { get; set; }
public override string ToString()
{
return string.Format("groupid : {0}, groupname : {1} ", GroupID, GroupName );
}
With in that class I also have an overridden Equals method in place.
public override bool Equals(object obj)
{
SecurityGroup secGroup = obj as SecurityGroup;
if (secGroup == null)
{
return false;
}
if (!string.Equals(GroupID, secGroup.GroupID, StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (!string.Equals(GroupName,secGroup.GroupName, StringComparison.OrdinalIgnoreCase))
{
return false;
}
I'm not sure if I need to post the class used for the nested loop but there's an IEquatable interface and the entireity of this class is EXACTLY like the SecurityGroup class I just posted.
//Compare IpPermissions
var diff1 = IpPermissions.Except(secGroup.IpPermissions);
var diff2 = secGroup.IpPermissions.Except(IpPermissions);
if (diff1.Any() || diff2.Any())
{
return false;
}
return true;
}
Now here's the Hashcode method that I set up:
public override int GetHashCode()
{
unchecked
{
const int HashingBase = (int)2166136261;
const int HashingMultiplier = 16777619;
int hash = HashingBase;
hash = (hash * HashingMultiplier) ^ (!Object.ReferenceEquals(null, IpPort) ? IpPort.GetHashCode() : 0);
hash = (hash * HashingMultiplier) ^ (!Object.ReferenceEquals(null, IpProtocol) ? IpProtocol.GetHashCode() : 0);
hash = (hash * HashingMultiplier) ^ (!Object.ReferenceEquals(null, IpRanges) ? IpRanges.GetHashCode() : 0);
return hash;
}
}
}
}
That essentially conludes the code I have in place to set up the framework for my comparesList method. Now here's where I'm having issues. I'm trying to set up the compare lists method and it's just giving me underscored red lines on the 'public static' part. The thing I can't figure out is the error it's giving me for the 'foreach' statements. It's saying, "Cannot convert element type 'AwsInstanceProfile1.Entity.SecurityGroup' to iterator type 'Amazon.EC2.Model.SecurityGroup'" Which is really weird because I should in theory have the framework set up to allow the lists into these objects. Here's the rest of the method:
public static bool CompareLists(List<Entity.SecurityGroup> list1, List<Entity.SecurityGroup> list2) =>
{
if (list1 == null || list2 == null)
return list1 == list2;
Dictionary<SecurityGroup, int> hash = new Dictionary<SecurityGroup, int>();
foreach (SecurityGroup secGroup in list1)
{
if (hash.ContainsKey(secGroup))
{
hash[secGroup]++;
}
else
{
hash.Add(secGroup, 1);
}
}
foreach (SecurityGroup secGroup in list2)
{
if (!hash.ContainsKey(secGroup) || hash[secGroup] == 0)
{
return false;
}
hash[secGroup]--;
}
return true;
}

C# Group By Several Nested Properties And List values

I have this object structure:
public class Root
{
public int Value1;
public int Value2;
public List<NestedA> NestedAList;
}
public class NestedA
{
public List<NestedB> NestedBList;
public List<NestedC> NestedCList;
}
public class NestedB{
public int ValueB;
public int ValueB2;
}
public class NestedC{
public int ValueC;
public int ValueC2;
}
I need to group root objects using all Values from Root class and it's nested lists.
I've been playing around a while and can't figure out how to/or if I can do this in a single group by statement, or what the best approach to acomplish this could be.
Edit: I need the items grouped by Root properties, Nested A Properties, Nested B Properties and Nested C Properties.
So it makes sense: My real objects have more properties, just showing the ones that I need grouped, and can use as a start point.
Thanks in advance.
If we have this element
Root
Value1 = 1
Value2 = 2
NestedAList = [
{NestedBList = [
{ValueB=2, ValueB2=3}
]
NestedCList = [
{ValueC=5, ValueC2=11}
]}
]
it should be grouped with this one:
Root
Value1 = 1
Value2 = 2
NestedAList = [
{NestedBList = [
{ValueB=2, ValueB2=3}
]
NestedCList = [
{ValueC=5, ValueC2=11}
]}
]
but not with this one:
Root
Value1 = 1
Value2 = 2
NestedAList = [
{NestedBList = [
{ValueB=2, ValueB2=3}, { ValueB= 1, ValueB2=4}
]
NestedCList = [
{ValueC=5, ValueC2=11}
]}
]
To accomplish this task, you can override Equals() and GetHashCode() methods for each class in your hierarchy. It may be little tricky, for example, like this:
public class Root
{
public int Value1;
public int Value2;
public List<NestedA> NestedAList;
public override bool Equals(object obj)
{
Root other = obj as Root;
if (other == null) return false;
return this.Value1 == other.Value1 && this.Value2 == other.Value2 && this.NestedAList.SequenceEqual(other.NestedAList);
}
public override int GetHashCode()
{
unchecked
{
int hasha = 19;
foreach (NestedA na in NestedAList)
{
hasha = hasha * 31 + na.GetHashCode();
}
return (Value1 ^ Value1 ^ hasha).GetHashCode();
}
}
}
public class NestedA
{
public List<NestedB> NestedBList;
public List<NestedC> NestedCList;
public override bool Equals(object obj)
{
NestedA other = obj as NestedA;
if (other == null) return false;
return NestedBList.SequenceEqual(other.NestedBList) && NestedCList.SequenceEqual(other.NestedCList);
}
public override int GetHashCode()
{
unchecked
{
int hashb = 19;
foreach (NestedB nb in NestedBList)
{
hashb = hashb * 31 + nb.GetHashCode();
}
int hashc = 19;
foreach (NestedC nc in NestedCList)
{
hashc = hashc * 31 + nc.GetHashCode();
}
return (hashb ^ hashc).GetHashCode();
}
}
}
public class NestedB{
public int ValueB;
public int ValueB2;
public override bool Equals(object obj)
{
NestedB other = obj as NestedB;
if (other == null) return false;
return this.ValueB == other.ValueB && this.ValueB2 == other.ValueB2;
}
public override int GetHashCode()
{
return (ValueB ^ ValueB2).GetHashCode();
}
}
public class NestedC{
public int ValueC;
public int ValueC2;
public override bool Equals(object obj)
{
NestedC other = obj as NestedC;
if (other == null) return false;
return this.ValueC == other.ValueC && this.ValueC2 == other.ValueC2;
}
public override int GetHashCode()
{
return (ValueC ^ ValueC2).GetHashCode();
}
}
After that you can easily select unique Roots (each unique Root represents a group):
roots.Distinct().ToList()
Same result using GoupBy():
roots.GroupBy(r => r).Select(g => g.First()).ToList()
Count elements in each group:
roots.GroupBy(r => r).Select(g => g.Count())
Enumerate elements in the first group:
roots.GroupBy(r => r).First().Select(g => g)
If you don't care about elements order in Lists, use Enumerable.All instead of SequenceEqual
EDIT: Also, in this case you have to change hash code generation alghoritm. For example, like this: hashb = hashb + nb.GetHashCode() * 31; (additional info about possible algorithms here)

Compare two custom LIST objects

I have scenario to check
1) if the any prop (EmployeeObject), from empDb appear in empXml , return true. Else return false
public class EmployeeObject
{
public Int32 Id { get; set; }
public string Title { get; set; }
public string Desc { get; set; }
.....
}
IList<EmployeeObject> empDb = PopulateFromDb(); //calling ado.net
IList<EmployeeObject> empXml = PopulateFromXml(); //deserializing xml
So let me get this straight, I'm trying to determine if list empXml is a subset of list empDb ?
Tried so far; but it returns false even thought i have check the data in both list and it should have return true unless i am doing something wrong in my expression.
//at least one MATCH
empDb.Any(a => empXml.Contains(a));
or
//at least one EXACT match
empDb.Any(x => empXml.Contains(y => x.Equals(y)));
If you don't have Equals and GetHashCode implemented in EmployeeObject class then employees will be compared by reference. And you will definitely have different instances here, because first list is created when you read data from database, and second list is created when you are deserializing xml. So, even employees with same values of all fields will be considered different.
If you want to check matches only by employee Id, then you can project sequences to ids and then use Intersect to check if match exist
// at least one employee with equal Id
empDb.Select(e => e.Id).Intersect(empXml.Select(e => e.Id)).Any()
If you want to compare employees by value of their fields instead of their references, you have several options. If you can't or don't want to change implementation of EmployeeObject class and override its Equals and GetHashCode methods, then you can create custom comparer for employees:
public class EmployeeComparer : IEqualityComparer<EmployeeObject>
{
public bool Equals(EmployeeObject x, EmployeeObject y)
{
return x.Id == y.Id
&& x.Title == y.Title
&& x.Desc == y.Desc;
}
public int GetHashCode(EmployeeObject obj)
{
int code = 19;
code = code * 23 + obj.Id.GetHashCode();
code = code * 23 + obj.Title.GetHashCode();
code = code * 23 + obj.Desc.GetHashCode();
return code;
}
}
Then you can use this comparer:
empDb.Intersect(empXml, new EmployeeComparer()).Any()
Or you can project your employees to anonymous objects (which have default implementation of Equals and GetHashCode):
empDb.Select(e => new { e.Id, e.Title, e.Desc })
.Intersect(empXml.Select(e => new { e.Id, e.Title, e.Desc })).Any()
Or override these methods:
public class EmployeeObject
{
public Int32 Id { get; set; }
public string Title { get; set; }
public string Desc { get; set; }
public override int GetHashCode()
{
int code = 19;
code = code * 23 + Id.GetHashCode();
code = code * 23 + Title.GetHashCode();
code = code * 23 + Desc.GetHashCode();
return code;
}
public override bool Equals(object obj)
{
EmployeeObject other = obj as EmployeeObject;
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
return Id == other.Id &&
Title == other.Title && Desc == other.Desc;
}
}
And your code will work. Or you can use Intersect:
empDb.Intersect(empXml).Any()
If Id is the primary key of the entity you might want to write:
var set = new HashSet<int>(empXml.Select(x => x.Id)); //For faster lookup
empDb.Any(a => set.Contains(a.Id));
But if you need to match on all properties you need to override Equals and GetHashCode. (this implementation also match on null values for the properties)
public class EmployeeObject : IEquatable<EmployeeObject>
{
public bool Equals(EmployeeObject other)
{
return Id == other.Id &&
string.Equals(Title, other.Title) &&
string.Equals(Desc, other.Desc);
}
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((EmployeeObject) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Id;
hashCode = (hashCode*397) ^ (Title != null ? Title.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (Desc != null ? Desc.GetHashCode() : 0);
return hashCode;
}
}
public Int32 Id { get; set; }
public string Title { get; set; }
public string Desc { get; set; }
}
And the write:
var set = new HashSet<EmployeeObject>(empXml); //For faster lookup
empDb.Any(a => set.Contains(a));

select from Dictionary finds value but select from ConcurrentDictionary does not

Today i was doing some tests with the ConcurrentDictionary and Dictionary:
class MyTest
{
public int Row { get; private set; }
public int Col { get; private set; }
public string Value { get; private set; }
public MyTest(int row, int col, string value)
{
this.Col = col;
this.Row = row;
this.Value = value;
}
public override bool Equals(object obj)
{
MyTest other = obj as MyTest;
return base.Equals(other);
}
public override int GetHashCode()
{
return (Col.GetHashCode() ^ Row.GetHashCode() ^ Value.GetHashCode());
}
}
Using the Entity above i created and filled a ConcurrentDictionary and a Dictionary and tried the code below:
ConcurrentDictionary<MyTest, List<MyTest>> _test = new ConcurrentDictionary<MyTest, List<MyTest>>();
Dictionary<MyTest, List<MyTest>> _test2 = new Dictionary<MyTest, List<MyTest>>();
MyTest dunno = _test.Values.AsParallel().Select(x => x.Find(a => a.Col == 1 && a.Row == 1)).FirstOrDefault();
MyTest dunno2 = _test2.Values.AsParallel().Select(x => x.Find(a => a.Col == 1 && a.Row == 1)).FirstOrDefault();
The first one returns the value but the second one not, what am i doing wrong?
This is the code used to add the values:
_test.AddOrUpdate(cell10,
new List<MyTest>
{
new MyTest(1, 1, "ovpSOMEVALUEValue"),
new MyTest(1, 2, "ocpSOMEVALUEValue")
},
(key, value) => value = new List<MyTest>());
_test2.Add(cell10,
new List<MyTest>
{
new MyTest(1, 1, "ovpSOMEVALUEValue"),
new MyTest(1, 2, "ocpSOMEVALUEValue")
}
);
You are calling AddOrUpdate, and your third parameter is that it should create a new empty list, thus the result is that you end up with an empty list. I am guessing that somewhere before this line of code you are adding an entry with the same key.
Also notice that the Equals function is incorrect. You are comparing on reference, not on the actual values you use in the GetHashCode. I suggest you use the default template for overriding Equals:
protected bool Equals(x other) {
return Row == other.Row && Col == other.Col && string.Equals(Value, other.Value);
}
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((x) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Row;
hashCode = (hashCode*397) ^ Col;
hashCode = (hashCode*397) ^ (Value != null ? Value.GetHashCode() : 0);
return hashCode;
}
}

Categories