0

I have a use case for filtering a set of objects based on multiple properties of the object. An approach that I would use in Javascript might be to create an object with only the properties that I care about, then check those property names against the objects I wish to filter.

Example (in Typescript):

example(): void {
    let template = {
        name: 'Test',
        color: 'Blue'
    };

    let objects = [
        { name: 'Test', color: 'Blue' },
        { name: 'Test2', color: 'Blue' },
        { name: 'Test', color: 'Red' },
        { name: 'Test', color: 'Blue', extra: true },
    ];

    let results = objects.map(o => this.compare(o, template));
    console.log(results);
}

compare(obj, template): boolean {
    for (const prop in template) {
        if (obj[prop] !== template[prop]) {
            return false;
        }
    }

    return true;
}

The above will write [true, false, false, true] to the console. The reason this is cool is that it will accept basically any object and issue the correct results. It's not obvious to me how to provide this feature in Java, such that I could avoid having to implement a comparison function on hundreds of data model beans.

Is there a built-in or commonly-used way to do the equivalent comparison in Java in a generic manner?

theMayer
  • 15,456
  • 7
  • 58
  • 90

2 Answers2

1

This can be done using reflection:

public static boolean compare(Object object, Object template) {
  Field[] templateFields = template.getClass().getDeclaredFields();
  try {
    for (Field templateField : templateFields) {
      Field objectField = object.getClass().getDeclaredField(templateField.getName());
      templateField.setAccessible(true);
      objectField.setAccessible(true);
      if (!objectField.get(object).equals(templateField.get(template))) {
        return false;
      }
    }
  } catch (Exception e) {
    return false;
  }
  return true;
}

(This code just shows the idea and doesn't cover all possible cases e.g. doesn't check inherited fields)

I'm not aware of built-in or commonly-used way to do this. There are utilities for strict comparison (usually intended for unit testing), not sure they will work for your use case.

Note that Java 9 modules can restrict reflective access to objects.

Alex Filatov
  • 4,768
  • 2
  • 16
  • 10
0

You can something like this using streams:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

class MyObjectTest {

    @Test
    void test() {
        MyObject template = new MyObject("Test", "Blue");

        List<MyObject> objects = new ArrayList<>();
        objects.add(new MyObject("Test", "Blue"));
        objects.add(new MyObject("Test2", "Blue"));
        objects.add(new MyObject("Test", "Red"));
        objects.add(new MyObject("Test", "Blue", true));

        List<MyObject> results = objects.stream()
                                        .filter(myObject -> myObject.getName().equals(template.getName())
                                                && myObject.getColor().equals(template.getColor()))
                                        .collect(Collectors.toList());
        System.out.println("results = " + results);
    }

    static class MyObject {
        private final String name;
        private final String color;
        private final boolean extra;

        public MyObject(String name, String color) {
            this.name = name;
            this.color = color;
            this.extra = false;
        }

        public MyObject(String name, String color, boolean extra) {
            this.name = name;
            this.color = color;
            this.extra = extra;
        }

        public String getName() {
            return name;
        }

        public String getColor() {
            return color;
        }
    }
}
Wim Deblauwe
  • 25,113
  • 20
  • 133
  • 211
  • Thanks for the answer - what I'm really looking for is how to implement the "compare" function similar to how it was done in Javascript, so it can be somewhat generic. The desire is actually to avoid having to stipulate a comparision function for each one of hundreds of data model classes. – theMayer Nov 15 '19 at 17:29
  • Would the "template" object and the objects in the list be of the same type? If not, you would need to build something that uses reflection to compare each field. AssertJ has something along that lines: [AbstractObjectAssert#isEqualToComparingOnlyGivenFields](http://joel-costigliola.github.io/assertj/core/api/org/assertj/core/api/AbstractObjectAssert.html#isEqualToComparingOnlyGivenFields(java.lang.Object,%20java.lang.String...)) – Wim Deblauwe Nov 15 '19 at 17:33
  • I would expect the template object to be of the type or supertype of the object to be checked - if it's not, then I think we can return false right away as the comparison might not be valid. – theMayer Nov 15 '19 at 18:13