Compare 2 Properties of equals object - c#

I have a class with 2 properties
public class SampleClass
{
public string Name { get; set; }
public List<Component> Components { get; set; }
}
And another class which is hold some string properties.
public class Component
{
public string Name { get; set; }
public string Age{ get; set; }
}
I have instance of this class created and added into a List
SampleClass classWithValues = new SampleClass();
var listComponent = new List<Component>();
listComponent.add(new Component{Name = "Random string",Age = "31"})
classWithValues.Components = listComponent;
classWithValues.Name = "TestName"
var listWithObjectClass = new List<SampleClass>();
listWithObjectClass.add(classWithValues);
Then i made a new instance of the SampleClass class and add exactly the same value into the properties :
SampleClass classWithValues1 = new SampleClass();
var listComponent1 = new List<Component>();
listComponent1.add(new Component{Name = "Random string",Age = "31"})
classWithValues1.Components = listComponent1;
classWithValues1.Name = "TestName";
And here is coming the strange part :
if I compare the property Names inside the list with the second instance of the Sample class with the new instance of the same class:
bool alreadyExists = listWithObjectClass.Any(x => x.Name == classWithValues1 .Name);
the result is true BUT
if I compare the List properties
bool alreadyExists = listWithObjectClass.Any(x => x.Components == classWithValues1.Components);
the result is false ?!
Can someone please give some information about this behavior.

Sorry my first answer was not quite right...
In order to get alreadyExist to be true you need to put in place property comparison in your classes as otherwise the equality comparison performed is the default reference comparison. Your objects contains the same property values but are actually different instances... The default equality comparison for objects is comparing references not content.
Try this...
void Main()
{
SampleClass classWithValues = new SampleClass();
var listComponent = new Components();
listComponent.Add(new Component{Name = "Random string",Age = "31"});
classWithValues.Components = listComponent;
classWithValues.Name = "TestName";
var listWithObjectClass = new List<SampleClass>();
listWithObjectClass.Add(classWithValues);
SampleClass classWithValues1 = new SampleClass();
var listComponent1 = new Components();
listComponent1.Add(new Component{Name = "Random string",Age = "31"});
classWithValues1.Components = listComponent1;
classWithValues1.Name = "TestName";
bool alreadyExists = listWithObjectClass.Any(x => x.Components.Equals(classWithValues1.Components));
}
public class SampleClass
{
public string Name { get; set; }
public Components Components { get; set; }
}
public class Component : IEquatable<Component>
{
public string Name { get; set; }
public string Age{ get; set; }
public bool Equals(Component otherComponent)
{
return Name == otherComponent.Name && Age == otherComponent.Age;
}
}
public class Components :List<Component>, IEquatable<Components>
{
public bool Equals(Components otherComponents)
{
if(this.Count!= otherComponents.Count) return false;
return this.TrueForAll(a=> otherComponents.Any(q=>q.Equals(a)))
&& otherComponents.TrueForAll(a=> this.Any(q=>q.Equals(a)));
}
}

The first comparison is about comparing the value of the two string. However, the second comparison is about Comparing two different object which their reference are different. Indeed, for the second comparison, compare their hashCode. To watch this, you can call .GetHashCode() for these two objects.
listComponent.GetHashCode() == listComponent1.GetHashCode() // false
listComponent[0].GetHashCode() == listComponent1[0].GetHashCode() // false

Related

How to get the properties with name, old value and new value after comparing two class objects

I have two objects with the same properties. What I need is the list of properties having a difference in values. Consider the following classes (there can be many properties but they will always be the same in both the classes). In the example, I have two objects with a difference in lastname and address. So, I need a list that contains the property name along with the old and new values.
public class A
{
public string ColumnName { get; set; }
public string oldValue { get; set; }
public string newValue { get; set; }
}
public class B
{
public string ColumnName { get; set; }
public string oldValue { get; set; }
public string newValue { get; set; }
}
public class C
{
public string ColumnName { get; set; }
public string oldValue { get; set; }
public string newValue { get; set; }
}
A objA = new A();
objA.firstName = "XYZ";
objA.lastName = "ABC";
objA.address = "123";
B objB = new B();
objB.firstName = "XYZ";
objB.lastName = "ABCD";
objB.address = "456";
List<C> listObj = new List<C>();
//Here I need a list of the columns having different value. For example this should be the result after comparison.
listObj.Add(new C{ColumnName = "lastName", oldValue = "ABC", newValue = "ABCD"});
listObj.Add(new C{ColumnName = "address", oldValue = "123", newValue = "456"});
Is there a way this can be achieved (preferably LINQ but not necessary)?
EDIT: For those looking for a solution, this is exactly what I was looking for: CompareNETObjects
The result of this LINQ is list of C class instances with different property name, old object value and new object value.
typeof(A).GetProperties().OrderBy(p => p.Name)
.Zip(typeof(B).GetProperties().OrderBy(p => p.Name),
((aInfo, bInfo) => !aInfo.GetValue(objA).Equals(bInfo.GetValue(objB))
? new C()
{
ColumnName = aInfo.Name,
oldValue = aInfo.GetValue(objA).ToString(),
newValue = bInfo.GetValue(objB).ToString()
}
: null))
.Where(x => !(x is null)).ToList();
More generall with anonymous class instead of C.
typeof(A).GetProperties().OrderBy(p => p.Name)
.Zip(typeof(B).GetProperties().OrderBy(p => p.Name),
((aInfo, bInfo) => !aInfo.GetValue(objA).Equals(bInfo.GetValue(objB))
? new
{
ColumnName = aInfo.Name,
oldValue = aInfo.GetValue(objA),
newValue = bInfo.GetValue(objB)
}
: null))
.Where(x => !(x is null)).ToList();

Reflection of object properties

i have this code
public class ParameterOrderInFunction : Attribute
{
public int ParameterOrder { get; set; }
public ParameterOrderInFunction(int parameterOrder)
{
this.ParameterOrder = parameterOrder;
}
}
public interface IGetKeyParameters
{
}
public class Person: IGetKeyParameters
{
[ParameterOrderInFunction(4)]
public string Age { get; set; }
public string Name { get; set; }
[ParameterOrderInFunction(3)]
public string Address { get; set; }
[ParameterOrderInFunction(2)]
public string Language { get; set; }
[ParameterOrderInFunction(1)]
public string City { get; set; }
public string Country { get; set; }
}
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Address = "my address";
person.Age = "32";
person.City = "my city";
person.Country = "my country";
Test t = new Test();
string result = t.GetParameter(person);
//string result = person.GetParameter();
Console.ReadKey();
}
}
public class Test
{
public string GetParameter(IGetKeyParameters obj)
{
string[] objectProperties = obj.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(ParameterOrderInFunction)))
.Select(p => new
{
Attribute = (ParameterOrderInFunction)Attribute.GetCustomAttribute(p, typeof(ParameterOrderInFunction), true),
PropertyValue = p.GetValue(this) == null ? string.Empty : p.GetValue(this).ToString()
})
.OrderBy(p => p.Attribute.ParameterOrder)
.Select(p => p.PropertyValue)
.ToArray();
string keyParameters = string.Join(string.Empty, objectProperties);
return keyParameters;
}
}
What i am trying to do is to get properties values as one string with some order .
it work fine if i put the function GetParameter inside the Person class.
however, i want to use the function GetParameter with other class as well,
so i create empty interface.
Now i want that every object that is of type IGetKeyParameters can use the function.
but i am getting exception in the line:
PropertyValue = p.GetValue(this) == null ? string.Empty : p.GetValue(this).ToString()
You should change loading properties from this (that doesn't have such properties) to parameter object:
PropertyValue = p.GetValue(obj) == null ? string.Empty : p.GetValue(obj).ToString()
You are passing the wrong reference as parameter to the method, you need to pass the object which you used to get the type and properties, so change:
p.GetValue(this) // this means pass current instance of containing class i.e. Test
to:
p.GetValue(obj)
Your statement p.GetValue(this) currenly means to pass the current instance of class Test as parameter which is i am pretty sure not what you want.
in your example code.

Comparing two lists "List<Category>" where Category is my class

I have to find out the difference between two lists of class Category.
My Category class has these properties:
public class Category
{
public int Id { get; set; }
public string Title { get; set; }
public bool IsQuantitative
{
get { return Products.Any(x => x.IsMultiPart); }
}
public List<Product> Products { get; set; }
public string Image { get; set; }
public string Description { get; set; }
}
Try this
var firstNotSecond = list1.Except(list2).ToList();
var secondNotFirst = list2.Except(list1).ToList();
link
I would sort the two lists by Id first.
Then run through it and compare the Objects.
This is a good Object Comparer:
https://www.nuget.org/packages/CompareNETObjects/
Here a little example:
//Here you set the config like you want to have it compared
ComparisonConfig comparisonConfig = new ComparisonConfig()
{
CompareChildren = true,
CompareFields = true,
CompareReadOnly = true,
CompareProperties = true,
MaxDifferences = 1,
MaxByteArrayDifferences = 1
};
CompareLogic comparer = new CompareLogic() { Config = comparisonConfig };
list1 = list1.OrderBy(x => x.Id).ToList();
list2 = list2.OrderBy(x => x.Id).ToList();
for (int i =0;i> list1.count;i++)
{
//Here you get a bool if the two Objects are Equal
bool areEqual = comparer.Compare(list1[i], list2[i]).AreEqual;
//Here you get a List of Differences Objects. It contains Values like "expected and "actual" etc.
var differences = comparer.Compare(list1[i], list2[i]).Differences;
//Here you handle Differences etc.
}

Updating Custom Class in List<T>

I am trying to update a List which is a List of Interfaces to concrete classes.
I add to the List each Market type i am interested in, for this Example these Markets are A and B
I loop over all the markets, (sample provided with 3 markets A B & C, we are only interested in A and B) And determine which is of interest to us.
Once found we pass this to an extraction method too do its work and create an instance of the Correct Market_ class type.
This all works fine, but when i try to update the list with the Updates it does not get reflected in the List.
Code below, any Suggestions?
Thanks
public class Test
{
public Test()
{
TheMarkets MarketsToUpdate = new TheMarkets();
List<SpecificCompanyMarket> lstMarks = new List<SpecificCompanyMarket>();
lstMarks.Add(new SpecificCompanyMarket(1234, "A", "Some HTML DATA HERE"));
lstMarks.Add(new SpecificCompanyMarket(5874, "B", "Some HTML DATA HERE"));
lstMarks.Add(new SpecificCompanyMarket(2224, "C", "Some HTML DATA HERE"));
foreach (var item in lstMarks)
{
if (MarketsToUpdate.IsMarketWeAreInterestedIn(item.MarketName))
{
ITheMarkets MarkToUpdate = ExtractMarketData(item);
var obj = MarketsToUpdate.MarketsWeAreInterestedIn.FirstOrDefault(x => x.MarketName() == "A");
if (obj != null)
{
obj = MarkToUpdate;
}
}
}
//Look At MarketsToUpdate Now and the item has not changed, still original values
//I was expecting to see the new values for the fields in A, not the default 0's
}
public ITheMarkets ExtractMarketData(SpecificCompanyMarket item)
{
ITheMarkets market = null;
if (item.MarketName.ToUpper() == "A")
{
Market_A marketType = new Market_A();
marketType.SomeValue1 = 123;
marketType.SomeValue2 = 158253;
market = marketType;
}
//Other Market extractions here
return market;
}
}
public class SpecificCompanyMarket
{
public int MarketId { get; set; }
public string MarketName { get; set; }
public string MarketDataHTML { get; set; }
public SpecificCompanyMarket(int MID, string MName, string MData)
{
MarketId = MID;
MarketName = MName;
MarketDataHTML = MData;
}
}
public class TheMarkets
{
public List<ITheMarkets> MarketsWeAreInterestedIn = new List<ITheMarkets>();
public TheMarkets()
{
Market_A A = new Market_A();
Market_B B = new Market_B();
MarketsWeAreInterestedIn.Add(A);
MarketsWeAreInterestedIn.Add(B);
}
public bool IsMarketWeAreInterestedIn(string strMarketName)
{
bool blnRetVal = false;
foreach (var item in MarketsWeAreInterestedIn)
{
if (item.MarketName().ToUpper().Trim().Equals(strMarketName.ToUpper().Trim()))
{
blnRetVal = true;
break;
}
}
return blnRetVal;
}
}
public interface ITheMarkets
{
string MarketName();
}
public class Market_A : ITheMarkets
{
public string LabelType { get; private set; }
public double SomeValue1 { get; set; }
public double SomeValue2 { get; set; }
public double SomeValue3 { get; set; }
public Market_A()
{
LabelType = "A";
}
public string MarketName()
{
return LabelType;
}
}
public class Market_B : ITheMarkets
{
public string LabelType { get; private set; }
public List<string> SomeList { get; set; }
public double SomeValue { get; set; }
public Market_B()
{
LabelType = "B";
}
public string MarketName()
{
return LabelType;
}
}
This is a short example to get you going. Loop through your list, find the object you want to update, create a new object of that type and then find the original objects index in the list and overwrite it in place. You are essentially just replacing the object in the list with a new one not mutating the existing one.
foreach (var item in lstMarks)
{
//your code to get an object with data to update
var yourObjectToUpdate = item.GetTheOneYouWant();
//make updates
yourObjectToUpdate.SomeProperty = "New Value";
int index = lstMarks.IndexOf(item);
lstMarks[index] = yourObjectToUpdate;
}
You are extracting an obj from marketWeAreInterestedIn list using LINQ's firstOrDefault extension. This is a new object and not a reference to the obj in that list. Therefore, no updates will be reflected in the object inside that list. Try using 'indexof'
You are not storing "list of interfaces" in your list. List<T> stores an array of pointers to objects that support T interface. Once you enumerate (with Linq in your case) your list, you copy a pointer from list, which is not associated with list itself in any way. It is just a pointer to your instance.
To do what you want, you will have to build new list while enumerating the original one, adding objects to it, according to your needs, so the second list will be based on the first one but with changes applied that you need.
You can also replace specific instance at specific index instead of building new list in your code, but to do this you will need to enumerate your list with for loop and know an index for each item:
list[index] = newvalue;
But there is a third solution to update list item directly by Proxying them. This is an example
class ItemProxy : T { public T Value { get; set; } }
var list = new List<ItemProxy<MyClass>>();
list.Insert(new ItemProxy { Value = new MyClass() });
list.Insert(new ItemProxy { Value = new MyClass() });
list.Insert(new ItemProxy { Value = new MyClass() });
foreach(var item in list)
if(item // ...)
item.Value = new MyClass(); // done, pointer in the list is updated.
Third is the best case for perfomance, but it will be better to use this proxying class for something more than just proxying.

How to copy a List<> to another List<> with Comparsion in c#

I an having Two Lists. I want to get the matched and unmatched values based on ID and add the results to another List. I can get both of these using Intersect/Except.
But I can get only ID in the resultant variables (matches and unmatches) . I need all the properties in the Template.
List<Template> listForTemplate = new List<Template>();
List<Template1> listForTemplate1 = new List<Template1>();
var matches = listForTemplate .Select(f => f.ID)
.Intersect(listForTemplate1 .Select(b => b.ID));
var ummatches = listForTemplate .Select(f => f.ID)
.Except(listForTemplate1.Select(b => b.ID));
public class Template
{
public string ID{ get; set; }
public string Name{ get; set; }
public string Age{ get; set; }
public string Place{ get; set; }
public string City{ get; set; }
public string State{ get; set; }
public string Country{ get; set; }
}
public class Template1
{
public string ID{ get; set; }
}
If you don't want to implement IEquality for this simple task, you can just modify your LINQ queries:
var matches = listForTemplate.Where(f => listForTemplate1.Any(b => b.ID == f.ID));
and
var unmatches = listForTemplate.Where(f => listForTemplate1.All(b => b.ID != f.ID));
You might want to check for null before accessing ID, but it should work.
You are looking for the overloaded function, with the second parameter IEqualityComparer. So make your comparer ( example: http://www.blackwasp.co.uk/IEqualityComparer.aspx ), and use the same comparer in intersect / except.
And for the generic part: maybe you should have a common interface for templates e.g. ObjectWithID describing that the class have a string ID property. Or simply use dynamic in your comparer (but I think this is very-very antipattern because you can have run time errors if using for the bad type).
You also have a problem: intersecting two collections with two different types will result in a collection of Object (common parent class). Then you have to cast a lot (antipattern). I advise you to make a common abstract class/interface for your template classes, and it is working. If you need to cast the elements back, do not cast, but use the visitior pattern: http://en.wikipedia.org/wiki/Visitor_pattern
Example (good):
static void Main(string[] args)
{
// http://stackoverflow.com/questions/16496998/how-to-copy-a-list-to-another-list-with-comparsion-in-c-sharp
List<Template> listForTemplate = new Template[] {
new Template(){ID = "1"},
new Template(){ID = "2"},
new Template(){ID = "3"},
new Template(){ID = "4"},
new Template(){ID = "5"},
new Template(){ID = "6"},
}.ToList();
List<Template1> listForTemplate1 = new Template1[] {
new Template1(){ID = "1"},
new Template1(){ID = "3"},
new Template1(){ID = "5"}
}.ToList();
var comp = new ObjectWithIDComparer();
var matches = listForTemplate.Intersect(listForTemplate1, comp);
var ummatches = listForTemplate.Except(listForTemplate1, comp);
Console.WriteLine("Matches:");
foreach (var item in matches) // note that item is instance of ObjectWithID
{
Console.WriteLine("{0}", item.ID);
}
Console.WriteLine();
Console.WriteLine("Ummatches:");
foreach (var item in ummatches) // note that item is instance of ObjectWithID
{
Console.WriteLine("{0}", item.ID);
}
Console.WriteLine();
}
}
public class ObjectWithIDComparer : IEqualityComparer<ObjectWithID>
{
public bool Equals(ObjectWithID x, ObjectWithID y)
{
return x.ID == y.ID;
}
public int GetHashCode(ObjectWithID obj)
{
return obj.ID.GetHashCode();
}
}
public interface ObjectWithID {
string ID { get; set; }
}
public class Template : ObjectWithID
{
public string ID { get; set; }
public string Name { get; set; }
public string Age { get; set; }
public string Place { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
public class Template1 : ObjectWithID
{
public string ID { get; set; }
}
Output:
Matches:
1
3
5
Ummatches:
2
4
6
Press any key to continue . . .
For comparison, this should also work (the first part is a variation on #MAV's answer):
var matches = from item in listForTemplate
join id in listForTemplate1 on item.ID equals id.ID
select item;
var unmatches = listForTemplate.Where(item => matches.All(elem => elem.ID != item.ID));
matches and unmatches will both be IEnumerable<Template> which is the type you require.
However, MAV's answer works fine so I'd go for that one.
As mentioned, Implement the IEqualityComparer<T> interface.
IEqualityComparer<T> MSDN
Then use this as an argument in your method for Except() and Intersect()
Intersect
There is a good example of how to do so on the link for the Intersect() method.
If you don't absolutely have to use LINQ, why not code something like this?
var matches = new List<Template>();
var unmatches = new List<Template>();
foreach (var entry in listForTemplate)
{
bool matched = false;
foreach (var t1Entry in listForTemplate1)
{
if (entry.ID == t1Entry.ID)
{
matches.Add(entry);
matched = true;
break;
}
}
if (!matched)
{
unmatches.Add(entry);
}
}
A disadvantage of the LINQ approach is that you're traversing the lists twice.

Categories

Resources