Conditional method call in the map method java 8 - java-8

Persons = personDao.getFileInformation(filePath)
.skip(1)
.map(this::getPerson)
.filter(person -> person != null)
.collect(Collectors.toList());
getFileInformation(filePath) returns Stream<String>
after reading lines in a file.
I would like to replace the getPerson method with a getMale or getFemale method based on the value of an enum
public enum gender {
male,female
}
How can this be achieved used lambda expressions?

If you just want to filter by gender (assuming there is an accessor like Person.getGender), then you only need to add a filter:
List<Person> malePeople;
malePeople = personDao.getFileInformation(filePath)
.skip(1)
.map(this::getPerson)
.filter(Objects::nonNull)
.filter(p -> p.getGender() == gender.male) // or gender.female
.collect(Collectors.toList());
If you rather want to group your results, the following will help you:
Map<gender, List<Person>> peopleByGender;
peopleByGender = personDao.getFileInformation(filePath)
.skip(1)
.map(this::getPerson)
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(Person::getGender));
now access all your female people with:
List<Person> femalePeople = peopleByGender.get(gender.female);
and the male ones with:
List<Person> malePeople = peopleByGender.get(gender.male);
If you just wanted to use a method to simplify the filter-predicate (p -> p.getGender() == gender.male), then you could use one of the following:
.filter(this::getMale) // or: YourClass::getMale for a static method
where this::getMale refers to the following method:
boolean getMale(Person p) {
return p.getGender() == gender.male;
}
or
.filter(getMale())
where getMale() refers to the following method:
Predicate<Person> getMale() {
return p -> p.getGender() == gender.male;
}

Related

Using the filter function in kotlin

so the past couple of hours, i have been trying to understand how the filter function works in kotlin and if it has any correlation with that of Java.
basically, i have a code that's written in java and i would love to have it transcribed to kotlin
private List<Order> getFilteredOrders(Courier courier) {
String[] glovoBoxKeywords = glovoBoxWords.toLowerCase().split(",");
List<Vehicle> allowedVehicles = Arrays.asList(MOTORCYCLE, ELECTRIC_SCOOTER);
return orders.stream()
.filter(order -> {
String description = order.getDescription().toLowerCase();
if (!courier.getBox()) {
return Arrays.stream(glovoBoxKeywords).noneMatch(description::contains);
}
return true;
})
.filter(order -> {
Location pickupLocation = order.getPickup();
Location deliveryLocation = order.getDelivery();
Double distance = calculateDistance(pickupLocation, deliveryLocation);
if (distance > longDeliveryDistance) {
return allowedVehicles.contains(courier.getVehicle());
}
return true;
})
.collect(Collectors.toList());
}
i tried this but i got at this, and was literally stuck :(
private fun findFilteredOrder(courier: Courier) : List<Order> {
val glovoBoxKeyWords = glovoBoxWords.toLowerCase().split(",")
val allowedVehicles = listOf(Vehicle.ELECTRIC_SCOOTER, Vehicle.MOTORCYCLE)
orderList.filter { order ->
val description = order.getDescription().toLowerCase()
if(!courier.getBox()) {
}
true
}.filter {
val pickupLocation = it.getPickup()
val deliveryLocation = it.getDelivery()
val distance = calculateDistance(deliveryLocation, pickupLocation)
if(distance > longDeliveryDistance) {
courier.getVehicle() in allowedVehicles
}
true
}
}
Please this is my first attempt and doing something with kotlin, so please go easy guys. thanks, also i'd be appreciative if anyone could help me with informative stuff as to how to understand these kotlin functions better. let, apply, associateBy... etc.. THANKS
The filter function in Kotlin Collections has the same principle as other frameworks/libraries, including Java Streams. Given a predicate (a function from the type of the collection to Boolean) it will return a new collection with the elements matching the predicate. You can find more information and examples of other functions and operators in the official documentation and here.
Your code was almost there, I translate the Java Stream operation to Kotlin List and rewrite the return statements to remove the redundant if
private fun findFilteredOrder(courier: Courier) : List<Order> {
val glovoBoxKeyWords = glovoBoxWords.toLowerCase().split(",")
val allowedVehicles = listOf(Vehicle.ELECTRIC_SCOOTER, Vehicle.MOTORCYCLE)
orderList.filter { order ->
val description = order.getDescription().toLowerCase()
courier.getBox() || glovoBoxKeywords.none { it in description }
}.filter { order ->
val pickupLocation = order.getPickup()
val deliveryLocation = order.getDelivery()
val distance = calculateDistance(deliveryLocation, pickupLocation)
distance <= longDeliveryDistance || courier.getVehicle() in allowedVehicles
}
}
I don't know why no one mentioned the use of labels: https://kotlinlang.org/docs/returns.html#break-and-continue-labels.
Since this question has a nice google ranking, I'll add what I was originally searching for.
The OP probably was aware that filter needs a predicate that returns a Boolean and that the filter will return a list with the items that pass the predicate (the items which the predicate returned true).
What he was not aware is that we can "emulate" Java returns through Kotlin labels:
private fun findFilteredOrder(courier: Courier) : List<Order> {
val glovoBoxKeyWords = glovoBoxWords.toLowerCase().split(",")
val allowedVehicles = listOf(Vehicle.ELECTRIC_SCOOTER, Vehicle.MOTORCYCLE)
orderList.filter shouldSkip#{ order ->
val description = order.getDescription().toLowerCase()
if (courier.getBox()) {
return#shouldSkip true
}
if (glovoBoxKeywords.none { it in description }) {
return#shouldSkip true
}
return#shouldSkip false
}.filter shouldSkip# { order ->
val pickupLocation = order.getPickup()
val deliveryLocation = order.getDelivery()
val distance = calculateDistance(deliveryLocation, pickupLocation)
if (distance <= longDeliveryDistance) {
return#shouldSkip true
}
if (courier.getVehicle() in allowedVehicles) {
return#shouldSkip true
}
return#shouldSkip false
}
}
Since Kotlin allows us to return in the last block line and the return keyword returns to the outer scope, it is pretty easy to:
filter {
startPutting >= someMagic && andComplex ||
verificationsThat.is { hardToUnderstand }.because {
weNeedToReturnHere
}
}
The labels allow us to be more verbose but also more clear.

Collect groupBy on deep property

private Map<String, Set<Square>> populateZuloSquare(List<Square> squares) {
if (squares == null || squares.isEmpty()) {
return emptyMap();
}
Map<String, Set<Square>> res = new HashMap<>();
squares.stream()
.filter(square -> {
if (square.getZuloCodes().isEmpty()) {
LOG("Ignored {}", square.id);
return false;
}
return true;
})
.forEach(square -> {
square.getZuloCodes()
.forEach(code -> {
res.putIfAbsent(code, new HashSet<>());
res.get(code).add(square);
}));
});
return Collections.unmodifiableMap(res);
}
The code above receives a list of Squares, and those squares may contain ZuloCodes inside. The output should be a immutable Map zuloCode and value all the squares with that UniquePrefix.
As you can see I cannot figure out a way to remove the auxiliar collection res and make the code easily readable, is there a way to explode that collection into a [zuloCode, square] and then collect.groupBy ? Also that if inside the filter is so unreadable, how would you tackle it?
The standard approach is using flatMap before collecting using groupingBy, but since you need the original Square for each element, you need to map to an object holding both, the Square instance and the zulo code String.
Since there is no standard pair or tuple type in Java (yet), a work-around is to use a Map.Entry instance, like this
private Map<String, Set<Square>> populateZuloSquare0(List<Square> squares) {
if (squares == null || squares.isEmpty()) {
return emptyMap();
}
return squares.stream()
.filter(square -> logMismatch(square, !square.getZuloCodes().isEmpty()))
.flatMap(square -> square.getZuloCodes().stream()
.map(code -> new AbstractMap.SimpleEntry<>(code, square)))
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toSet())),
Collections::unmodifiableMap));
}
private static boolean logMismatch(Square square, boolean match) {
if(!match) LOG("Ignored {}", square.id);
return match;
}
An alternative is to use a custom collector which will iterate over the keys:
private Map<String, Set<Square>> populateZuloSquare(List<Square> squares) {
if (squares == null || squares.isEmpty()) {
return emptyMap();
}
return squares.stream()
.filter(square -> logMismatch(square, !square.getZuloCodes().isEmpty()))
.collect(Collector.of(
HashMap<String, Set<Square>>::new,
(m,square) -> square.getZuloCodes()
.forEach(code -> m.computeIfAbsent(code, x -> new HashSet<>()).add(square)),
(m1,m2) -> {
if(m1.isEmpty()) return m2;
m2.forEach((key,set) ->
m1.merge(key, set, (s1,s2) -> { s1.addAll(s2); return s1; }));
return m1;
},
Collections::unmodifiableMap)
);
}
Note that this custom collector can be seen as a parallel capable variant of the following looping code:
private Map<String, Set<Square>> populateZuloSquare(List<Square> squares) {
if (squares == null || squares.isEmpty()) {
return emptyMap();
}
Map<String, Set<Square>> res = new HashMap<>();
squares.forEach(square -> {
if(square.getZuloCodes().isEmpty()) LOG("Ignored {}", square.id);
else square.getZuloCodes().forEach(
code -> res.computeIfAbsent(code, x -> new HashSet<>()).add(square));
});
return Collections.unmodifiableMap(res);
}
which might not look so bad now, when you don’t need the code to be parallel capable…
How about this. You may use map merge operation to get this thing done. I have updated the filter and simplified it too.
squares.stream().filter(s -> !s.getZuloCodes().isEmpty())
.forEach(s -> s.getZuloCodes().stream().forEach(z -> res.merge(z, new HashSet<>(Arrays.asList(s)),
(s1, s2) -> Stream.of(s1, s2).flatMap(Collection::stream).collect(Collectors.toSet()))));

How can I concatenate string elements from list in java8 based on their amount?

I have the following structure:
class MyClass {
String name;
String descr;
public String getName() {
return name;
}
}
Now I have a List of those objects and I want to print the name from the object above if the list contains any of those elements.
This is my code so far:
List<MyClass> list = getList();
if (list != null && list.size() > 0) {
System.out.println(list.get(0).getName());
} else {
System.out.println("list is empty");
}
This will work when the list contains only one element. Now I need to improve it and consider an example when there is more than one element - in that case I need to print all names, comma separated.
For example the output should be:
When there are 3 elements:
name1,name2,name3
when there's one element:
name1
and when there's none:
list is empty
what's the most efficient way of implementing it?
You could use Collectors.joining combined with Collectors.collectingAndThen:
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.joining;
...
String res =
list.stream()
.map(c -> c.name)
.collect(collectingAndThen(joining(","), s -> s.isEmpty() ? "list is empty" : s));
If you want to take in account the option when the list is null, you could do:
String res =
Optional.ofNullable(list).map(l -> l.stream()...).orElse("list is empty");
but to be honest I would use an if statement beforehand:
if(list == null || list.isEmpty()) {
return "list is empty";
} else {
return list.stream().map(c -> c.name).collect(joining(","));
}
You also can use the built-in class StringJoiner to simplify your code as following code:
StringJoiner joiner = new StringJoiner(",");
joiner.setEmptyValue("list is empty");
list.forEach(it -> joiner.add(it.getName()));
System.out.println(joiner);

Compare value objects using a custom comparator

I want to compare value objects in a way that null properties are ignored.
More specifically, I only want to include those properties of a value object in the comparison which are not-null on the right side.
E.g.
class Person {
String name;
String surname;
}
Person personLeft = new Person();
personLeft.name = "John";
personLeft.surname = "Doe";
Person personRight = new Person();
personRight.name="John"
// this should NOT yield any changes
javers.compare(personLeft, personRight);
// that comparison however, will show that surname has changed
javers.compare(personRight, personLeft);
I though I could tackle this problem by writing a custom comparator and registering it for Person.
Unfortunately this comparator is never called.
Stumbling upon that post, I fear that this is not possible using a custom comparator for the Person class.
Instead I would have to register custom comparators for all containing value types of the Person class, i.e. String.
Is that the intended use of javers or are there any alternatives to that approach?
What is important here is that CustomValueComparator can be used only for Value types. So you have 2 options:
Map Person as Value and then you implement diff algorithm for
Person. It works but is awkward because Person is more like an
Entity.
Register a CustomValueComparator for Strings (all Strings), and then you can use Person as Entity. That's the option that I would choose.
See how it works (groovy):
class Entity {
#Id int id
Person person
}
class Person {
String name
}
def "should use CustomValueComparator for Person when Person is mapped as Value"(){
given:
def javers = JaversBuilder.javers().registerValue(Person,
{ l, r -> if (r.name == null) return true
else return r.name.equals(l.name)
}).build()
def personLeft = new Person(name: "john")
def personRight = new Person()
def eLeft = new Entity(id:1, person: personLeft)
def eRight = new Entity(id:1, person: personRight)
expect:
javers.compare(eLeft, eRight).changes.size() == 0
javers.compare(eRight, eLeft).changes.size() == 1
javers.getTypeMapping(Person) instanceof ValueType
}
def "should used CustomValueComparator for Strings"(){
given:
def javers = JaversBuilder.javers().registerValue(String,
{ l, r -> if (r == null) return true
else return r.equals(l)
}).build()
def personLeft = new Person(name: "john")
def personRight = new Person()
expect:
javers.compare(personLeft, personRight).changes.size() == 0
javers.compare(personRight, personLeft).changes.size() == 1
javers.getTypeMapping(Person) instanceof ValueObjectType
}

Modify property value of the objects in list using Java 8 streams

I have a list of Fruit objects in ArrayList and I want to modify fruitName to its plural name.
Refer the example:
#Data
#AllArgsConstructor
#ToString
class Fruit {
long id;
String name;
String country;
}
List<Fruit> fruits = Lists.newArrayList();
fruits.add(new Fruit(1L, "Apple", "India"));
fruits.add(new Fruit(2L, "Pineapple", "India"));
fruits.add(new Fruit(3L, "Kiwi", "New Zealand"));
Comparator<Option> byNameComparator = (e1, e2) -> e1.getName().compareToIgnoreCase(e2.getName());
fruits = fruits.stream().filter(fruit -> "India".equals(fruit.getCountry()))
.sorted(byNameComparator).collect(Collectors.toList());
List<Fruit> fruitsWithPluralNames = Lists.newArrayList();
for (Fruit fruit : fruits) {
fruit.setName(fruit.getName() + "s");
fruitsWithPluralNames.add(fruit);
}
System.out.println(fruitsWithPluralNames);
// which prints [Fruit(id=1, name=Apples, country=India), Fruit(id=2, name=Pineapples, country=India), Fruit(id=3, name=Kiwis, country=New Zealand)]
Do we have any way to achieve same behavior using Java 8 streams ?
If you wanna create new list, use Stream.map method:
List<Fruit> newList = fruits.stream()
.map(f -> new Fruit(f.getId(), f.getName() + "s", f.getCountry()))
.collect(Collectors.toList())
If you wanna modify current list, use Collection.forEach:
fruits.forEach(f -> f.setName(f.getName() + "s"))
You can use just forEach. No stream at all:
fruits.forEach(fruit -> fruit.setName(fruit.getName() + "s"));
You can use peek to do that.
List<Fruit> newList = fruits.stream()
.peek(f -> f.setName(f.getName() + "s"))
.collect(Collectors.toList());
just for modifying certain property from object collection you could directly use forEach with a collection as follows
collection.forEach(c -> c.setXyz(c.getXyz + "a"))
We can change the property via map without creating new objects.
Below method increase the age by 2. It will modify your original list
List<Employee> l2=list.stream().map(t->{
t.setAge(t.getAge()*2);
return t;
}
).collect(Collectors.toList());
You can do it using streams map function like below, get result in new stream for further processing.
Stream<Fruit> newFruits = fruits.stream().map(fruit -> {fruit.name+="s"; return fruit;});
newFruits.forEach(fruit->{
System.out.println(fruit.name);
});
You can use map from streams.
fruits.map(i-> {
i.setFruit();
return i;
}).collect(Collectors.toList();

Resources