How to Compare Two Objects and Find the Fields/Properties Changed

Compare two objects for properties with different values

I improved a little on Krishnas answer:

public List<string> GetChangedProperties<T>(object A, object B)
{
if (A != null && B != null)
{
var type = typeof(T);
var allProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var allSimpleProperties = allProperties.Where(pi => pi.PropertyType.IsSimpleType());
var unequalProperties =
from pi in allSimpleProperties
let AValue = type.GetProperty(pi.Name).GetValue(A, null)
let BValue = type.GetProperty(pi.Name).GetValue(B, null)
where AValue != BValue && (AValue == null || !AValue.Equals(BValue))
select pi.Name;
return unequalProperties.ToList();
}
else
{
throw new ArgumentNullException("You need to provide 2 non-null objects");
}
}

because it wasn't working for me. This one does and the only other thing you need to make it work is the IsSimpleType()-Extension Method that I adapted from this answer here (I only converted it into an extension method).

public static bool IsSimpleType(this Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
// nullable type, check if the nested type is simple.
return type.GetGenericArguments()[0].IsSimpleType();
}
return type.IsPrimitive
|| type.IsEnum
|| type.Equals(typeof(string))
|| type.Equals(typeof(decimal));
}

Comparing two objects for varying set of properties

anyMatch

You can solve this with the Stream API, using anyMatch, with your rules basically being defined as Predicates.

For example checking if there is any person who is 20 years old:

List<Person> persons = ...

boolean doesAnyMatch = persons.stream()
.anyMatch(p -> p.getAge() == 20);

You can of course also setup the rule in a way that it compares with an existing item, mimicking equals a bit more:

p -> p.getAge() == otherPerson.getAge()


Predicate

You can setup all your rules somewhere else, as Predicates and then use them. For example:

List<Predicate<Person>> rules = List.of(
p -> p.getAge() == 20,
p -> p.getName().equals("John"),
p -> p.getAge() > 18,
p -> p.getName().length() > 10 && p.getAge() < 50
);

And then maybe use them in some sort of loop, whatever you need:

for (Predicate rule : rules) {
boolean doesAnyMatch = persons.stream()
.anyMatch(rule);
...
}


findAny

You can substitute anyMatch by a combination of filter and findAny to receive an actual match, i.e. a Person, instead of just a boolean:

Person matchingPerson = persons.stream()
.filter(rule)
.findAny();

Compare fields in two Object and update one to the other in Java

There are no really utility that can "find and apply" the differences (as far as I know), however, there is reflection (as other said), like Apache Comparator (which is a powerfull class).

https://commons.apache.org/proper/commons-collections/javadocs/api-3.2.1/org/apache/commons/collections/ComparatorUtils.html

As for "applying differences", there is also a tool in Apache (BeanUtils this time): http://commons.apache.org/proper/commons-beanutils/ which is "copyProperties(Object o1, Object o2)" wihchi will copy all properties (fields) from one object to another.

So, you just have to compare the objects and if their are differences, copy one into the other.

Except for performance problems, I don't see why you would copy only the fields that are differents (because if you copy the fields that are the same, well there won't be any difference). If you have to copy only the field that are different, I guess you'll have to make your own tool.

How to efficiently compare two objects of same Class and check which are the fields that differ?

If you extend the ideas to compare the object graph , it is not a trivial problem. So, the efficient way is not to re-inventing the wheel but use an existing library such as JaVers :

Member oldMember = new Member("foo" ,"chan" ,"AB12" , 21 ,LocalDateTime.now());
Member newMember = new Member("bar" ,"chan" ,"AB12" , 22 ,LocalDateTime.now());

Diff diff = javers.compare(oldMember, newMember);
for(Change change: diff.getChanges()) {
System.out.println(change);
}

Then , you can get something like:

ValueChange{ 'firstName' changed from 'foo' to 'bar' }
ValueChange{ 'age' changed from '21' to '22' }

Compare two objects excluding some fields - Java

The quickest way without writing any code is Lombok

Lombok is one of the most used libraries in java and it takes a lot of Boilerplate code off your projects. If you need to read more on what it can and does, go here.

The way to implement what you need is pretty straightforward:

// Generate the equals and HashCode functions and Include only the fields that I annotate with Include
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public class Class1
{

@EqualsAndHashCode.Include // Include this field
private Long identity;

private String testStr1; // This field is not annotated with Include so it will not be included in the functions.

// ... any other fields
}

Lombok can do a lot more than this. For more information on @EqualsAndHashCode refer to this.

You can always use @EqualsAndHashCode.Exclude for a quicker solution to your use case:

@EqualsAndHashCode
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public final class Class1 {
private String a;
private String b;
private String c;
:
:
:

private String z;

@EqualsAndHashCode.Exclude
private Date createdAt;
@EqualsAndHashCode.Exclude
private Date updatedAt;
}

JS compare two objects. Return key of changed value(s)

You can try using a filter:

Object.keys(a).filter( key => a[key] !== b[key] )

This assumes that a and b have the same keys. Any new keys that are in a will show up as changed.

Compare Properties of classes to identify which have changed

A simple solution based on reflection. Note that it could be optimized, and it doesn't support (at this time) comparing inner collections/objects. The compared object must be POD (Plain Old Data)

public class Project
{
public string Name { get; set; }
public TaskStatus Status { get; set; }
public string Planner { get; set; }
public DateTime ScheduleStart { get; set; }
public DateTime ScheduleEnd { get; set; }
public double EstimatedCost { get; set; }
public double ActualCost { get; set; }
public string AssignedTo { get; set; }

public Project Clone()
{
// If your object has inner collections, or
// references to other objects, you'll have to deep
// clone them ***manually***!!!
return (Project)MemberwiseClone();
}
}

public static class SimpleComparer
{
// Item1: property name, Item2 current, Item3 original
public static List<Tuple<string, object, object>> Differences<T>(T current, T original)
{
var diffs = new List<Tuple<string, object, object>>();

MethodInfo areEqualMethod = typeof(SimpleComparer).GetMethod("AreEqual", BindingFlags.Static | BindingFlags.NonPublic);

foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
object x = prop.GetValue(current);
object y = prop.GetValue(original);
bool areEqual = (bool)areEqualMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { x, y });

if (!areEqual)
{
diffs.Add(Tuple.Create(prop.Name, x, y));
}
}

return diffs;
}

private static bool AreEqual<T>(T x, T y)
{
return EqualityComparer<T>.Default.Equals(x, y);
}
}

Now, you'll need a Clone() method:

public class Project
{
public string Name { get; set; }
public TaskStatus Status { get; set; }
public string Planner { get; set; }
public DateTime ScheduleStart { get; set; }
public DateTime ScheduleEnd { get; set; }
public double EstimatedCost { get; set; }
public double ActualCost { get; set; }
public string AssignedTo { get; set; }

public Project Clone()
{
// If your object has inner collections, you'll have to deep
// clone them ***manually***!!!
return (Project)MemberwiseClone();
}
}

and then...

var current = new Project();
var original = current.Clone();
current.ActualCost = 10000;

var diffs = SimpleComparer.Differences(current, original);

foreach (var diff in diffs)
{
Console.WriteLine("'{0}' changed from {1} to {2}", diff.Item1, diff.Item3, diff.Item2);
}


Related Topics



Leave a reply



Submit