Is there a way to check if all objects in a list have the same attribute with Google Guava API?
Moreover, is there a way to send more parameters to Predicate?
Let's said I want to filter all my objects with string that I am getting from the user,
and I want the Predicate to use this parameter when applying the filter.
You can create your own predicate as follows:
class MyPredicate implements Predicate<MyObject> {
private final String parameter;
public MyPredicate(String parameter) {this.parameter = parameter;}
boolean apply(MyObject input) {
// apply predicate using parameter.
}
}
You can then filter by doing:
Iterables.filter(myIterable, new MyPredicate(myParameter));
You should be wary though that this performs a lazy filter.
Is there a way to check if all objects in a list have the same attribute with Google Guava API?
Yes:
Foo first = list.get(0).getFoo();
boolean allSameFoo = Iterables.all(list, element -> element.getFoo().equals(first));
Or, if you're not using Java 8 yet:
final Foo first = list.get(0).getFoo();
boolean allSameFoo = Iterables.all(list, new Predicate<Bar> {
#Override
public boolean apply(Bar element) {
return element.getFoo().equals(first);
}
});
is there a way to send more parameters to Predicate
Yes:
String s = getFromUser();
Iterables.filter(list, element -> element.getFoo().equals(s));
Or, if you're not using Java 8 yet:
final String s = getFromUser();
Iterables.filter(list, new Predicate<Bar> {
#Override
public boolean apply(Bar element) {
return element.getFoo().equals(s);
}
});
It seems you don't really know how inner classes work, so you should read the tutorial about them: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html.
Related
Background
I am writing an OpenRewrite recipe to add some comments to the Java code. To avoid inserting the comment at unnecessary points, I have written the following code (it detects the already existing comment) and it worked properly:
public class JtestSuppressDelombokVisitor extends JavaIsoVisitor<ExecutionContext> {
#Override
public MethodDeclaration visitMethodDeclaration(MethodDeclaration methodDecl, ExecutionContext context) {
// (snip)
Iterator<Comment> it = methodDecl.getPrefix().getComments().iterator();
boolean alreadyHasSuppressComment = false;
while (it.hasNext()) {
Comment comment = it.next();
PrintOutputCapture<String> p = new PrintOutputCapture<String>("");
comment.printComment(this.getCursor(), p);
if (p.out.toString().matches(".*parasoft-begin-suppress\sALL.*")) {
alreadyHasSuppressComment = true;
break;
}
}
// (snip)
return methodDecl;
}
}
Problem
I have tried to refactor the code above with the Stream API. The code needs the result of this.getCursor() in the process, but I couldn't find the way to pass it to the instance of Predicate:
boolean alreadyHasSuppressComment = methodDecl.getPrefix().getComments().stream()
.anyMatch(new Predicate<Comment>() {
#Override
public boolean test(Comment comment) {
PrintOutputCapture<String> p = new PrintOutputCapture<String>("");
comment.printComment(this.getCursor(), p); // <- Can't call `this.getCursor()` on the `JtestSuppressDelombokVisitor` class in the `Predicate`
return p.out.toString().matches(".*parasoft-begin-suppress\sALL.*");
}
});
Question
Is there any way to pass the object other than the object on the stream from outside to Predicate?
Or, it is impossible to write such a code with the Stream API?
You need to specify the outer class because the sole this keyword refers to the implemented anonymous Predicate class.
comment.printComment(JtestSuppressDelombokVisitor.this.getCursor(), p);
I know, I can do several things in JEXL, but unable to find Filter feature in it, which is indeed very useful.
How can I do something like
var x=[{a:11,b=5},{a:1,b=15},{a:12,b=25},{a:4,b=35},{a:7,b=45}];
return x[.a>10].b; // Which filters to {a:11,b=5} & {a:12,b=25}
// & hence returns [5,25]
First of all your syntax is not valid JEXL. I assume you meant this:
var x = [{'a':11,'b':5}, {'a':1,'b':15}, {'a':12,'b':25}, {'a':4,'b':35}, {'a':7,'b':45}];
Since you can call any Java method on any object in a JEXL script, you have (at least theoretically) full access to the Java Stream API.
However, the Stream API isn't directly available from a raw array and we can't just call Arrays.stream(x); without some effort. The easiest way around this is to create a set instead:
var x = {{'a':11,'b':5}, {'a':1,'b':15}, {'a':12,'b':25}, {'a':4,'b':35}, {'a':7,'b':45}};
Now we can simply call stream() and work from there:
x.stream();
What we want now is something like this:
x.stream().filter(function(m){m['a']>10});
Unfortunately the method resolver in JEXL will not be able to correctly resolve Stream.filter(Predicate) with a JEXL function, as it doesn't know how to turn a JEXL function into a Predicate. A JEXL function is of type org.apache.commons.jexl3.internal.Closure.
Thus the very least you need to do is to provide your own Predicate implementation in Java and then create a new instance in your script:
public class MyCustomFilterPredicate implements Predicate<HashMap<String, Integer>> {
#Override
public boolean test(final HashMap<String, Integer> m)
{
return m.get("a") > 10;
}
}
You can then create a new instance in your JEXL script:
var filterPredicate = new('my.custom.filter.predicate.MyCustomFilterPredicate');
The same goes for Stream.map(Function):
public class MyCustomMapFunction implements Function<HashMap<String, Integer>, Integer> {
#Override
public Integer apply(final HashMap<String, Integer> m)
{
return m.get("b");
}
}
And again create a new instance in your script:
var mapFunction = new('my.custom.map.function.MyCustomMapFunction');
Your entire script will then look like this:
var x = {{'a':11,'b':5}, {'a':1,'b':15}, {'a':12,'b':25}, {'a':4,'b':35}, {'a':7,'b':45}};
var filterPredicate = new('my.custom.filter.predicate.MyCustomFilterPredicate');
var mapFunction = new('my.custom.map.function.MyCustomMapFunction');
return x.stream().filter(filterPredicate).map(mapFunction).toArray();
Of course you might have noticed that the reusability of your predicate and function implementations are rather limited. This is why I'd recommend creating implementations that wrap a JEXL Closure:
public class MyCustomFilterPredicate implements Predicate<Object> {
private final Closure closure;
public MyCustomFilterPredicate(final Closure closure) {
this.closure = closure;
}
#Override
public boolean test(final Object o)
{
return (boolean) closure.execute(JexlEngine.getThreadContext(), o);
}
}
public class MyCustomMapFunction implements Function<Object, Object> {
private final Closure closure;
public MyCustomMapFunction(final Closure closure) {
this.closure = closure;
}
#Override
public Object apply(final Object o)
{
return closure.execute(JexlEngine.getThreadContext(), o);
}
}
Now you can change your script as follows and reuse these Java classes in various ways:
var x = {{'a':11,'b':5}, {'a':1,'b':15}, {'a':12,'b':25}, {'a':4,'b':35}, {'a':7,'b':45}};
var filterPredicate = new('my.custom.filter.predicate.MyCustomFilterPredicate', function(m){m['a']>10});
var mapFunction = new('my.custom.map.function.MyCustomMapFunction', function(m){m['b']});
return x.stream().filter(filterPredicate).map(mapFunction).toArray();
I have the ViewValue class defined as follows:
class ViewValue {
private Long id;
private Integer value;
private String description;
private View view;
private Double defaultFeeRate;
// getters and setters for all properties
}
Somewhere in my code i need to convert a list of ViewValue instances to a list containing values of id fields from corresponding ViewValue.
I do it using foreach loop:
List<Long> toIdsList(List<ViewValue> viewValues) {
List<Long> ids = new ArrayList<Long>();
for (ViewValue viewValue : viewValues) {
ids.add(viewValue.getId());
}
return ids;
}
Is there a better approach to this problem?
We can do it in a single line of code using java 8
List<Long> ids = viewValues.stream().map(ViewValue::getId).collect(Collectors.toList());
For more info : Java 8 - Streams
You could do it in a one-liner using Commons BeanUtils and Collections:
(why write your own code when others have done it for you?)
import org.apache.commons.beanutils.BeanToPropertyValueTransformer;
import org.apache.commons.collections.CollectionUtils;
...
List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues,
new BeanToPropertyValueTransformer("id"));
Use google collections. Example:
Function<ViewValue, Long> transform = new Function<ViewValue, Long>() {
#Override
public Long apply(ViewValue from) {
return from.getId();
}
};
List<ViewValue> list = Lists.newArrayList();
List<Long> idsList = Lists.transform(list, transform);
UPDATE:
On Java 8 you don't need Guava. You can:
import com.example.ViewValue;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
Function<ViewValue, Long> transform = ViewValue::getId;
List<ViewValue> source = new ArrayList<>();
List<Long> result = source.stream().map(transform).collect(Collectors.toList());
Or just:
List<ViewValue> source= new ArrayList<>();
List<Long> result = source.stream().map(ViewValue::getId).collect(Collectors.toList());
NEXT UPDATE (The last one after Javaslang to Vavr name change):
Currently it's worth to mention about the solution with Javaslang library(http://www.javaslang.io/) Vavr library (http://www.vavr.io/). Let's assume that we have our list with genuine objects:
List<ViewValue> source = newArrayList(new ViewValue(1), new ViewValue(2), new ViewValue(2));
We could make transformation with List class from Javaslang library (on the long run the collect is not convenient):
List<Long> result = io.vavr.collection.List.ofAll(source).map(ViewValue::getId).toJavaList();
But you will see the power with only the Javaslang lists:
io.vavr.collection.List<ViewValue> source = javaslang.collection.List.of(new ViewValue(1), new ViewValue(2), new ViewValue(3));
io.vavr.collection.List<Long> res = source.map(ViewValue::getId);
I encourage to take a look available collections and new types on that library (I like especially the Try type). You will find the documentation under the following address: http://www.javaslang.io/javaslang-docs/ http://www.vavr.io/vavr-docs/.
PS. Due to the Oracle and the "Java" word within the name they had to change the library name from javaslang to something else. They had decided to Vavr.
EDIT: This answer is based on the idea that you'll need to do similar things for different entities and different properties elsewhere in your code. If you only need to convert the list of ViewValues to a list of Longs by ID, then stick with your original code. If you want a more reusable solution, however, read on...
I would declare an interface for the projection, e.g.
public interface Function<Arg,Result>
{
public Result apply(Arg arg);
}
Then you can write a single generic conversion method:
public <Source, Result> List<Result> convertAll(List<Source> source,
Function<Source, Result> projection)
{
ArrayList<Result> results = new ArrayList<Result>();
for (Source element : source)
{
results.add(projection.apply(element));
}
return results;
}
Then you can define simple projections like this:
private static final Function<ViewValue, Long> ID_PROJECTION =
new Function<ViewValue, Long>()
{
public Long apply(ViewValue x)
{
return x.getId();
}
};
And apply it just like this:
List<Long> ids = convertAll(values, ID_PROJECTION);
(Obviously using K&R bracing and longer lines makes the projection declaration a bit shorter :)
Frankly all of this would be a lot nicer with lambda expressions, but never mind...
I've implemented a small functional library for this usecase. One of the methods has this signature:
<T> List<T> mapToProperty(List<?> objectList, String property, Class<T> returnType)
Which takes the string and uses reflection to create a call to the property then it returns a List backed by the objectList where get and iterator implemented using this property call.
The mapToProperty functions is implemented in terms of a general map function that takes a Function as a mapper though, just as another post described. Very usefull.
I suggest you read up on basic functionl programming and in particular take a look at Functors (objects implementing a map function)
Edit: Reflection really doesn't have to be expensive. The JVM has improved a lot in this area. Just make sure to compile the invocation once and reuse it.
Edit2: Sample code
public class MapExample {
public static interface Function<A,R>
{
public R apply(A b);
}
public static <A,R> Function<A,R> compilePropertyMapper(Class<A> objectType, String property, Class<R> propertyType)
{
try {
final Method m = objectType.getMethod("get" + property.substring(0,1).toUpperCase() + property.substring(1));
if(!propertyType.isAssignableFrom(m.getReturnType()))
throw new IllegalArgumentException(
"Property "+property+" on class "+objectType.getSimpleName()+" is not a "+propertyType.getSimpleName()
);
return new Function<A,R>()
{
#SuppressWarnings("unchecked")
public R apply(A b)
{
try {
return (R)m.invoke(b);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
};
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T1,T2> List<T2> map(final List<T1> list, final Function<T1,T2> mapper)
{
return new AbstractList<T2>()
{
#Override
public T2 get(int index) {
return mapper.apply(list.get(index));
}
#Override
public int size() {
return list.size();
}
};
}
#SuppressWarnings("unchecked")
public static <T1,T2> List<T2> mapToProperty(List<T1> list, String property, Class<T2> propertyType)
{
if(list == null)
return null;
else if(list.isEmpty())
return Collections.emptyList();
return map(list,compilePropertyMapper((Class<T1>)list.get(0).getClass(), property, propertyType));
}
}
You could use a wrapper:
public class IdList impements List<Long>
{
private List<ViewValue> underlying;
pubic IdList(List<ViewValue> underying)
{
this.underlying = underying;
}
public Long get(int index)
{
return underlying.get(index).getId()
}
// other List methods
}
Though that's even more tedious work, it could improve performance.
You could also implement your and my solution generic-ly using reflection, but that would be very bad for performance.
There's no short and easy generic solution in Java, I'm afraid. In Groovy, you would simply use collect(), but I believe that involves reflection as well.
That depends on what you then do with the List<Long>, and the List<ViewValue>
For example you might get sufficient functionality from creating your own List implementation that wraps a List<ViewValue>, implementing iterator() with an iterator implementation that iterates over the ViewValues, returning the id.
You can populate a map from the properties of a list of objects (say id as key and some property as value) as below
Map<String, Integer> mapCount = list.stream().collect(Collectors.toMap(Object::get_id, Object::proprty));
This is very easy if I just want to base this on absolute equality. I'd just do:
collectionA.removeAll(collectionB).
However, let's say I have this object:
class Item {
private String color;
private String name;
private String type;
}
And two collections...
List<Item> items1, List<item> items2.
...but I just want to remove everything from item1 that has the same name and type as something in item2.
Note that I can't subclass or define equals, hashcode for this class.
I'd want this to be the same complexity of the existing collections.removeAll method.
The best solution I can think of would be something like:
class SimpleItem {
String name;
String type;
Item item;
public SimpleItem(Item item) {
this.name = item.getName();
this.type = item.getType();
}
#Override
public boolean equals(Object obj) {
...
}
#Override
public int hashCode() {
...
}
}
Set<SimpleItem> simpleItems1 = ...;
for (Item item : items1) {
simpleItems1.add(new SimpleItem(item));
}
Set<SimpleItem> simpleItems2 = ...;
for (Item item : items2) {
simpleItems2.add(new SimpleItem(item));
}
simpleItems1.removeAll(simpleItems2);
Set<Item> items = ...;
for (SimpleItem simpleItem : simpleItems) {
items.add(simpleItem.item);
}
...but that is insanely verbose. It's Java 8. What clever solution am I missing?
You mention that it's Java 8. In that case you have a very simple and straightforward way to achieve this:
list1.removeIf(item1 -> list2.stream().anyMatch(item2 -> customEquals(item1, item2));
If your customEquals method is a member of Item you could use a method reference to make it a bit neater:
list1.removeIf(item -> list2.stream().anyMatch(item::customEquals));
In your case you could put your condition directly into the statement rather than creating a separate method:
list1.removeIf(item1 -> list2.stream().anyMatch(item2 ->
item1.getName().equals(item2.getName()) && item1.getType().equals(item2.getType())));
Conveniently removeIf is a default member of the Collection interface so any class that implements Collection should support it as long as the implementation's iterator supports remove.
You can use apache commons class to do so. Good examples are here
Another link
Is it possible to pass an argument to a lambdaj Predicate?
public static Matcher<SomeObject> isSpecialObject = new Predicate<SomeObject>() {
public boolean apply(SomeObject specialObj) {
return LIST_OF_SPECIAL_IDS.contains(specialObj.getTypeId());
}
};
I would like to alter the above predicate so I can pass in a list, rather than use the static list LIST_OF_SPECIAL_IDS. Is that possible?
Thanks.
I suspect you want something like:
public static Matcher<SomeObject> createPredicate(final List<String> ids) {
return new Predicate<SomeObject>() {
public boolean apply(SomeObject specialObj) {
return ids.contains(specialObj.getTypeId());
}
};
}
You've got to make it a method rather than just a field, as otherwise you've got nowhere to pass the list. The parameter has to be final so that you can use it within the anonymous inner class.