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 Predicate
s.
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 Predicate
s 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
Java.Sql.Sqlexception: Field Doesn't Have a Default Value
Simplifying the If-Else Condition Using Java Functional Style Programming
Keytool Error Bash: Keytool: Command Not Found
Scheduling a Job With Spring Programmatically (With Fixedrate Set Dynamically)
Typecasting an Object from Parent Class to Child
Spring Boot Application Shutdown Immediate After Starting
How to Change Cookie Processor to Legacycookieprocessor in Tomcat 8
Spring Webclient: How to Stream Large Byte[] to File
Deserialization With @Jsonsubtypes for No Value - Missing Property Error
How to Do Name Validation Allowing Alphabet, Spaces and Dot in Android
Automatically Convert Style Sheets to Inline Style
Can a @Manytoone JPA Relation Be Null
How to Get My Loops to Display Horizontally Instead of Vertically
How to Accept Only Numeric Values in a Jtextfield
Hibernate Error - Querysyntaxexception: Users Is Not Mapped [From Users]