Match List of objects against list of properties - java

I'm trying to use hamcrest matchers to match a list of objects against a list/array of their properties. For one property value this is not a problem, because I can do something like this:
assertThat(savedGroup.getMembers(),
containsInAnyOrder(hasProperty("name", is(NAMES[0]))));
For multiple property values I can use multiple hasProperty() calls
assertThat(savedGroup.getMembers(),
containsInAnyOrder(
hasProperty("name", is(NAMES[0])),
hasProperty("name", is(NAMES[1]))));
But is there a generic way to match against all values in the NAMES array?

The best way (IMO) to do this would be to combine the overloaded containsInAnyOrder Matcher along with a custom FeatureMatcher. Ultimately your code would look like this:
String[] expectedNames = new String[] { "John", "Bob", "Carol"};
assertThat(savedGroup.getMembers(), hasNames(expectedNames));
hasNames is implemented as follows:
private Matcher<Iterable<? extends Member>> hasNames(String[] expectedNames) {
return containsInAnyOrder(Arrays.stream(expectedNames).map(name -> name(name)).collect(Collectors.toList()));
}
And the final part is the call to name which generates a Matcher that will extract a property in a type-safe way from your object:
private Matcher<Member> name(String name) {
return new FeatureMatcher<Member, String>(equalTo(name), "name", "name") {
#Override
protected String featureValueOf(Member actual) {
return actual.getName();
}
};
}
The benefit of doing it this is way is that:
You get the benefit of type-safety instead of using hasProperty
Your test now describes what you actual want to match on, i.e. hasNames
The code produced is now more flexible and composable. Want to match a single objects name? All you now need to do is assertThat(member, has(name("Fred")))
You can get even more composability by moving the equalTo sub-matcher to be part of the hasNames call like this:
private Matcher<Iterable<? extends Member>> hasNames(String[] expectedNames) {
return containsInAnyOrder(Arrays.stream(expectedNames).map(name -> name(equalTo(name))).collect(Collectors.toList()));
}
private Matcher<Member> name(Matcher<String> nameMatcher) {
return new FeatureMatcher<Member, String>(nameMatcher, "name", "name") {
#Override
protected String featureValueOf(Member actual) {
return actual.getName();
}
};
}

One of containsInAnyOrder's overloads accepts a collection of matchers as its argument. Thus you could do something like this:
assertThat(
savedGroup.getMembers(),
containsInAnyOrder(
Stream.of(NAMES)
.map(name -> hasProperty("name", is(name)))
.collect(Collectors.toList())
));
(if using Java 8, otherwise would need to add a loop building up the collection)

Need to make some cleanup (description output), but I think it does solve your problem:
package org.example.matchers;
import java.util.List;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.TypeSafeMatcher;
public class ContainsArrayElementsInAnyOrder<T> extends TypeSafeMatcher<List<T>> {
private T[] toMatch;
public ContainsArrayElementsInAnyOrder(final T[] toMatch) {
this.toMatch = toMatch;
}
#Override
protected boolean matchesSafely(List<T> item) {
if(item.size() != toMatch.length) {
return false;
}
for (T t : toMatch) {
if(!item.contains(t)) {
return false;
}
}
return true;
}
#Override
public void describeMismatchSafely(List<T> item, Description mismatchDescription) {
mismatchDescription.appendValueList("[", ",", "]", item);
}
#Override
public void describeTo(Description description) {
description.appendValueList("[", ",", "]", toMatch);
}
#Factory
public static <T> ContainsArrayElementsInAnyOrder<T> containsArrayElementsInAnyOrder(T[] elements) {
return new ContainsArrayElementsInAnyOrder<T>(elements);
}
}
Test:
#Test
public void shouldContainsInAnyOrderSameElementsInArrayAsInList() {
final String[] NAME = new String[]{"name3", "name1", "name2"};
final List<String> result = new ArrayList<>(3);
result.add("name2");
result.add("name1");
result.add("name4");
assertThat(result, containsArrayElementsInAnyOrder(NAME));
}
Output if not match:
java.lang.AssertionError:
Expected: ["name3","name1","name2"]
but: ["name2","name1","name4"]
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:956)
at org.junit.Assert.assertThat(Assert.java:923)
at ..

Related

In Java, can I make a predicate that applies a filter on more than one object?

I have a predicate that I use to filter a list of the same Entity Object:
Predicate<DWHDeal> companyFilter = i -> i.getCompany().equals(company);
I also have to apply the same filter, with the exact same condition on the exact same field, on a list of DTOs where the DTOS is built based on the entity from before:
Predicate<DWHDealDTO> companyFilterDTO = i -> i.getCompany().equals(company);
Is it possible to achieve this without instancing two different predicates? If possible, I would like to achieve this by making only one Predicate.
Assuming getCompany() returns a String you could create Predicate<String>:
Predicate<String> predicate = s -> s.equals(company);
And then using it like:
list.stream()
.filter(dto -> predicate.test(dto.getCompany()))
...
But there is not much benefit since it requires almost the same code.
If equality is only check then you can use static Predicate isEqual(Object targetRef). see java doc https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#isEqual-java.lang.Object-
class StudentView{
String name;
public StudentView(String name) {
this.name = name;
}
}
class StudentDTO{
String name;
public StudentDTO(String name) {
this.name = name;
}
}
public void testPredicate(){
StudentView studentView= new StudentView("John");
StudentDTO studentDTO = new StudentDTO("Sam");
Predicate p = Predicate.isEqual("John");
System.out.println("Test for Student View "+ p.test(studentView.name));
System.out.println("Test for Student DTO "+ p.test(studentDTO.name));
}
I think you will need a Function<T,R> before using Predicate :
There are two concepts to Function. First is a java.util.function.Function which accepts one argument and produces a result. The second is stream intermediate operation map which converts each element in a stream into another object via the supplied function.
In your case the Function should look like :
Function<DWHDeal, DWHDealDTO> myFunction = new Function<DWHDeal, DWHDealDTO>() {
public DWHDealDTO apply(DWHDeal t) {
return ... ;
}
};
I tried the basic Program as below with success:
static class DWHDeal{
String name;
public DWHDeal(String name) {
this.name = name;
}
}
static class DWHDealDTO{
String name;
public DWHDealDTO(String name) {
this.name = name;
}
}
static Predicate<DWHDealDTO> companyFilter = i -> i.name.equalsIgnoreCase("com");
public static void main(String[] args) {
Function<DWHDeal, DWHDealDTO> myFunction = new Function<DWHDeal, DWHDealDTO>() {
public DWHDealDTO apply(DWHDeal t) {
return new DWHDealDTO("com");
}
};
DWHDeal newDWHDealDTOObj = new DWHDeal("com");
System.out.println(companyFilter.test(myFunction.apply(newDWHDealDTOObj))); //Works
}
As suggested in the comments, the common interface would be the preferred solution.
I guess you could do something like this, but to be fair, it is ugly.
private String getCompany(Object o) {
if(o instanceof DWHDeal)
return ((DWHDeal) o).getCompany();
else
return ((DWHDealDTO) o).getCompany();
}
Predicate<Object> companyFilter = i -> getCompany(i).equals(company);

Pass a function with lamba expression to make a generic function

I am using google's AutoValue to generate some configs, but before generating the configs, I would like to make sure that the entries are sanitized and default values are added to the list.
The AutoValue part looks like:
#AutoValue.Builder
public abstract static class Builder {
public abstract Builder primaryName(List<String> value);
public abstract Optional<List<String>> primaryName();
public abstract Builder primaryTitle(List<String> value);
abstract Optional<List<String>> primaryTitle();
abstract Config autoBuild();
public Config build() {
normalizePriorities();
EntitySourcePrioritizationConfig config = autoBuild();
return config;
}
I have the following code that is repeated in normalizePriorities():
private void normalizePriorities()
{
normalizeName();
normalizeTitle();
}
private void normalizeName() {
if (!primaryName().isPresent()) {
primaryName(defaultPrimaryNames());
} else {
List<String> providedConfigEntries = primaryName().get();
List<String> cleanConfig = sanitizeConfig(providedConfigEntries);
primaryName(cleanConfig);
}
}
private void normalizeTitle() {
if (!primaryTitle().isPresent()) {
primaryTitle(defaultPrimaryTitles());
} else {
List<String> providedConfigEntries = primaryTitle().get();
List<String> cleanConfig = sanitizeConfig(providedConfigEntries);
primaryTitle(cleanConfig);
}
}
I was wondering how I could use lambda expressions in order to reduce the deduplication of code.
The default names and titles are just a list of Strings as follows that could be passed as parameters:
public static ImmutableList<String> defaultPrimaryTitles() {
return ImmutableList.of(
"BBA",
"TNN");
}
public static ImmutableList<String> defaultPrimaryNames() {
return ImmutableList.of(
"SNS Inc.",
"ABC Corp.");
}
I have tried to generify the function like so:
normalize(primaryAlias(), defaultPrimaryTitles());
private void normalize(Optional<List<String>> configList, List<String> defaultConfig){
...
}
Unfortunately, I am not too sure how to generify and pass public abstract Builder primaryTitle(List<String> value) into the method.
You could pass a Consumer that accepts a list of strings and calls the builder methods, e.g.:
Create the consumer and pass it to the normalize method:
Consumer<List<String>> builderConsumer = (x) -> Builder.primaryName(x);
normalize(primaryAlias(), defaultPrimaryTitles(), builderConsumer);
And the normalize method:
private void normalize(ImmutableList<String> configList, List<String> defaultConfig, Consumer<List<String>> builderConsumer) {
List<String> cleanConfig = new ArrayList<>();
// ...
builderConsumer.accept(cleanConfig);
}

method returning either a collection or a single value

I have a class with various properties and I would like to write a wrapper method around them in order to loop around them more easily.
Some properties return a collection of values, some a single value. And I'm looking for the best approach for this.
My first approach is to let the wrapper method return whatever the property getters return.
public class Test {
public Object getValue(String propName) {
if ("attr1".equals(propName)) return getAttribute1();
else if ("attr2".equals(propName)) return getAttribute2();
else return null;
}
public List<String> getAttribute1() {
return Arrays.asList("Hello","World");
}
public String getAttribute2() {
return "Goodbye";
}
public static void main(String[] args) {
final Test test=new Test();
Stream.of("attr1","attr2")
.forEach(p-> {
Object o=test.getValue(p);
if (o instanceof Collection) {
((Collection) o).forEach(v->System.out.println(v));
}
else {
System.out.println(o);
}
});
}
}
The bad point with this approach is that the caller has to test himself whether the result is a collection or not.
Other approach, seamless for the caller, is to always return a collection, ie. the wrapper function wraps the single values into a Collection. Here an HashSet, but we can imagine an adhoc, minimum 1 element list.
public class TestAlt {
public Collection getValue(String propName) {
if ("attr1".equals(propName))
return getAttribute1();
else if ("attr2".equals(propName)) {
Set s = new HashSet();
s.add(getAttribute2());
return s;
}
else
return null;
}
public List<String> getAttribute1() {
return Arrays.asList("Hello", "World");
}
public String getAttribute2() {
return "Goodbye";
}
public static void main(String[] args) {
final TestAlt test = new TestAlt();
Stream.of("attr1", "attr2")
.forEach(p -> {
test.getValue(p).forEach(v -> System.out.println(v));
});
}
Performance-wise, design-wise, ... what's your opinion on these approaches ? Do you have better ideas ?
Well, you could pass the action to be performed on each attribute to the object and let the object decide on how to handle it. E.g.:
in Class Test:
public void forEachAttribute(String propName, Handler h) {
if ("attr1".equals(propName))
h.handle(getAttribute1());
else if ("attr2".equals(propName)) {
getAttribute2().forEach(o -> h.handle(o))
}
}
And a class Handler with the function handle(String s), that does, what you want to do.
If you cannot edit Test, you can also move the function outside Test
public void forEachTestAttribute(Test t, String propName, Handler h)...
Performance-wise: This removes an if-clause
Design-wise: This removes a cast, but creates more classes.
*Edit: It also maintains type-security, and if there are multiple kinds of attributes (String, int, etc.) you could add more handle-functions, to still maintain type-security.
Regarding the design I would rewrite your code into this:
TestAlt.java
import java.util.*;
import java.util.stream.Stream;
public class TestAlt {
private Map<String, AttributeProcessor> map = AttributeMapFactory.createMap();
public Collection getValue(String propName) {
return Optional
.ofNullable(map.get(propName))
.map(AttributeProcessor::getAttribute)
.orElse(Arrays.asList("default")); //to avoid unexpected NPE's
}
public static void main(String[] args) {
final TestAlt test = new TestAlt();
Stream.of("attr1", "attr2")
.forEach(p -> test.getValue(p).forEach(v -> System.out.println(v)));
}
}
AttributeMapFactory.java
import java.util.HashMap;
import java.util.Map;
public class AttributeMapFactory {
public static Map<String, AttributeProcessor> createMap() {
Map<String, AttributeProcessor> map = new HashMap<>();
map.put("attr1", new HiAttributeProcessor());
map.put("attr2", new ByeAttributeProcessor());
return map;
}
}
AttributeProcessor.java
import java.util.Collection;
public interface AttributeProcessor {
Collection<String> getAttribute();
}
HiAttributeProcessor.java
import java.util.Arrays;
import java.util.Collection;
public class HiAttributeProcessor implements AttributeProcessor{
#Override
public Collection<String> getAttribute() {
return Arrays.asList("Hello", "World");
}
}
ByeAttributeProcessor.java
import java.util.Arrays;
import java.util.Collection;
public class ByeAttributeProcessor implements AttributeProcessor{
#Override
public Collection<String> getAttribute() {
return Arrays.asList("Goodbye");
}
}
The main point is that you get rid of if-else statements using map and dynamic dispatch.
The main advantage of this approach is that your code becomes more flexible to further changes. In case of this small programm it does not really matter and is an overkill. But if we are talking about large enterprise application, then yes, it becomes crucial.

Java: How can I define an anonymous Hamcrest Matcher?

I'm trying use JUnit / Hamcrest to assert that a collection contains at least one element that my custom logic asserts is true. I'm hoping there's some kind of Matcher like 'anyOf' that takes a lambda (or anonymous class definition) where I can define the custom logic. I've tried TypeSafeMatcher but can't figure out what to do with it.
I don't think that anyOf is what I'm looking for either as that seem to take a list of Matchers.
what are you testing? There's a good chance you could use a combination of matchers like hasItem, allOf and hasProperty, otherwise you could implement org.hamcrest.TypeSafeMatcher. I find looking at the source code of existing matchers helps. I've created a basic custom matcher below that matches on a property
public static class Foo {
private int id;
public Foo(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
#Test
public void customMatcher() {
Collection<Foo> foos = Arrays.asList(new Foo[]{new Foo(1), new Foo(2)});
assertThat(foos, hasItem(hasId(1)));
assertThat(foos, hasItem(hasId(2)));
assertThat(foos, not(hasItem(hasId(3))));
}
public static Matcher<Foo> hasId(final int expectedId) {
return new TypeSafeMatcher<Foo>() {
#Override
protected void describeMismatchSafely(Foo foo, Description description) {
description.appendText("was ").appendValue(foo.getId());
}
#Override
public void describeTo(Description description) {
description.appendText("Foo with id ").appendValue(expectedId);
}
#Override
protected boolean matchesSafely(Foo foo) {
// Your custom matching logic goes here
return foo.getId() == expectedId;
}
};
}
Perhaps Matchers.hasItems() can help you?
List<String> strings = Arrays.asList("a", "bb", "ccc");
assertThat(strings, Matchers.hasItems("a"));
assertThat(strings, Matchers.hasItems("a", "bb"));
Matchers also have a method for providing other Matcher as arguments, i.e. hasItems(Matcher<? super >... itemMatchers).
Moreover, there are some methods working on arrays hasItemInArray(T element) and hasItemInArray(Matcher<? super > elementMatcher)

Need to loop over an array/list/whatever and *return to caller* each element -but the loop only runs once, of course

I'm obviously missing something here, as this sound basic enough but yet...
I have a collection of objects . I need to use each one of them as parameter in constructor for a new object and return each new object to the caller method, one by one.
But -if I loop over the collection obviously the loop only runs once, and only returns the 1st object.
Edit : Returning the whole collection or some new collection will not work because :
The caller method [not mine to change] runs inside a start() method of a Runnable ThingProvider, which returns a single Thing whenever a request is submitted to it. So, returning List is not possible.
Thanks :)
public List<T> loop(Collection<? extends U> coll) {
List<T> a = new ArrayList<T>();
for (U u : coll){
a.add(new T(u));
}
return a;
}
Return a custom Iterator. Assumming your new objects are of class MyObject and the constructor accepts an Object:
public Iterator<MyObject> myObjectsIterator(final Iterator<? extends Object> it) {
return new Iterator<MyObject>() {
public boolean hasNext() {
return it.hasNext();
}
public MyObject next() {
return new MyObject(it.next());
}
public void remove() {
it.remove();
}
};
}
And you would call it like this:
...
Iterator<MyObject> myIt = myObjectsIterator(myListOfObjects.iterator());
// Now you can pass myIt around as a normal object. It will remember
// which one is the next Object with which to construct a MyObject
// and will generate it on the fly
...
while (myIt.hasNext()) { // is there any MyObject remaining?
MyObject myObj = myIt.next(); // gets the next MyObject
// do something with myObj
}
...
This is a poorly worded question and I think as others have noted, just returning a new list of the objects is fine. But if you really want to process them one at a time while you're looping through it, you can use the command pattern.
public interface Command {
void execute(NewType object);
}
Now in your caller method, you can do the following:
public void doSomething() {
processList(myList, new Command() {
void execute(NewType object) {
// Do whatever you want with this object
}
});
}
And, in the method that will actually go through the list:
public void processList(Iterable<OldType> values, Command command) {
for(OldType v : values) {
NewType newType = new NewType(v);
command.execute(newType);
}
}
In java you can return only once. So if you want to get some informations from your methods either you wrap them into a "Big" Object (here a List) or you give to the method the means to put informations in your parameters.
You could have something like this :
public static void main(String... args){
List<Parameter> parameters = methodToGetParameters();
List<Result> results = generateObjectsFromList(parameters);
for(Result result : results){
handleAResult(result);
}
}
public List<Result> generateObjectsFromList(List<Parameter> parameters){
List<Result> results = new ArrayList<Result>();
for(Parameter parameter : parameters){
results.add(new Result(parameter));
}
return results;
}
Or like this :
public static void main(String... args){
List<Parameter> parameters = methodToGetParameters();
List<Result> results = new ArrayList<Result>();
generateObjectsFromList(parameters, results);
for(Result result : results){
handleAResult(result);
}
}
public void generateObjectsFromList(List<Parameter> parameters, List<Result> results){
for(Parameter parameter : parameters){
results.add(new Result(parameter));
}
}
A third way to do this would be to use fields, but it's not really good to have a lot of fields if they're not really used (or only by one method).
On the same topic :
Java Object Oriented Design Question: Returning multiple objects in java(Updated)
Using a java method to return multiple values?
Return a collection from the method and in the collection implement a custom iterator to transform the input collection to the new collection. The following code shows how to do it using the Google Guava library:
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
public class Test {
static class Person {
public final String name;
public Person(String name) {
this.name = name;
}
}
public static Collection<Person> peopleFromNames(Collection<String> names) {
return Collections2.transform(names, new Function<String, Person>() {
public Person apply(String name) {
return new Person(name);
}});
}
public static void main(String[] args) {
List<String> names = Arrays.asList("Brian", "Albert", "Roger");
for (Person person : peopleFromNames(names)) {
System.out.println(person.name);
}
}
}
do you mean using of delegates something like below
public class Test {
private static class Person{
private final String name;
Person(String name){
this.name = name;
}
#Override
public String toString() {
return return name;
}
}
private interface Printer {
void print(Object object);
}
public static void main(String[] args) {
final String[] names = {"one", "two", "three"};
final ArrayList<Person> people = construct(names, new Printer() {
#Override
public void print(Object object) {
System.out.println(object.toString());
}
});
}
private static ArrayList<Person> construct(String[] names, Printer printer) {
ArrayList<Person> people = new ArrayList<Person>();
for (String name : names) {
printer.print(new Person(name));
}
return people;
}
}
It's Possible.
Check these Project for Java-yield , yield4Java, infomancers
If you're using this just once in your entire code, You're better off choosing a method from the other answers.
Return a list of the new objects.

Categories