Java collection aggregate using lambdas - java

I am new to java 8 lamdas. I need help on the below collection aggregation and sorted list.
I have Arraylist of object which has List
[
[parentName1,parentkey1, child1Name1, childKey1],
[parentName1,parentkey1, child1Name2, childKey2],
[parentName3,parentkey3, child1Name3, childKey3],
[parentName3,parentkey3, child1Name4, childKey4],
[parentName5,parentkey5, child1Name5, childKey5],
[parentName5,parentkey5, child1Name6, childKey6]
]
I would like aggregate above collections using java 8 lambdas in to sorted list.
List(Parent(parentName1,parentkey1, List( Child(child1Name1,childKey1),Child(child1Name2, childKey2) ),
Parent(parentName3,parentkey3, List( Child(child1Name3,childKey3),Child(child1Name4, childKey4) ),
Parent(parentName5,parentkey5, List( Child(child1Name5,childKey5),Child(child1Name5, childKey6) )
);
Any help would appreciated.

I did not understand sorting criteria using which you wanted to sort the list but here is generic example but with little modification it ll work for your case also.
employeeList.stream()
.sorted((e1, e2) -> Integer.compare(e1.getEmployeeNumber(), e2.getEmployeeNumber()))
.forEach(e -> System.out.println(e));
Above example shows examplyee object can be sorted based employee number, but you can change the logic to fit your case.

Related

Java 11 Filter a String entry from an Array of String does not work

I would like to remove a String entry and an empty String from the Array of Strings.
My array of String contains the following values from index 0 to 3 - 'Name','First','Last', ""
Here is what I tried, using the stream API and Java 11 Predicate.not API:
Arrays.asList(myStringArray).stream()
.filter(Predicate.not(String::isEmpty).or(entry -> entry.equals("Name")))
.collect(Collectors.toList());
I would expect the entries "Name" and "" to be removed from myStringArray, what am I missing?
Another possibility:
var newList = new ArrayList<>(Arrays.asList(myStringArray));
newList.removeAll(List.of("", "Name"));
Or, if you know that "Name" is always the first entry and "" is always the last entry, you could do this, which has better performance as it doesn't take any copies of anything:
var newList = Arrays.asList(myStringArray).subList(1, myStringArray.length - 1)
My question formation may have been poor, sorry about that and here's what I was looking for and it produces the desired result that I need -
Arrays.asList(myStringArray).stream()
.filter(Predicate.not(String::isEmpty))
.filter(Predicate.not(entry -> entry.equals("Name")))
.collect(Collectors.toList());
Another way to do this without doing two filter calls can be:
Arrays.asList(myStringArray).stream()
.filter(Predicate.not(String::isEmpty).and(entry -> !entry.equals("Name")))
.collect(Collectors.toList())
I think the problem you have, is that you are not taking resulting list. The thing is, the items are not removed from the array, but new list is created with items without removed items.
so just do:
var newList = Arrays.asList(myStringArray).stream()
.filter(Predicate.not(String::isEmpty)).filter(Predicate.not(entry -> entry.equals("Name")))
.collect(Collectors.toList());```

Map duplicate keys error fixing using streams in java 8

I get an PublicException: Duplicate Keys error in this place.
Map<BgwContract, List<Fee>> bgwContractFeeMap = bgwContractList
.stream()
.filter(bgwContract -> !bgwContract.getStatus().equals(BgwContractStatus.CLOSED))
.filter(bgwContract -> availableIbans.contains(bgwContract.getFeeAccount()))
.collect(
Collectors.toMap(bgwContract -> bgwContract,
bgwContractFeeService::getContractMonthlyFees)
);
I understand that the issue is that there are some duplicates and it immediately crashes. I know that a .distinct() would fix this error, but I don't want to lose any data. Is there a way how to enhance this mapping to fix this error without loosing any values, maybe some kind of a filter or any other kind of java 8 methods? I'm not talking about MultiMaps etc.
You need to pass a merge function to Collectors.toMap(), which handles values having the same key:
Map<BgwContract, List<Fee>> bgwContractFeeMap = bgwContractList
.stream()
.filter(bgwContract -> !bgwContract.getStatus().equals(BgwContractStatus.CLOSED))
.filter(bgwContract -> availableIbans.contains(bgwContract.getFeeAccount()))
.collect(
Collectors.toMap(Function.identity(),
bgwContractFeeService::getContractMonthlyFees,
(l1,l2)->{
l1.addAll(l2);
return l1;
})
);
In this case, the elements of two value lists having the same key will be concatenated into a single list.

Java 8 Stream API Filtering

I have a collection of objects, with the following type:
{
String action_name; //add or delete
long action_time;
String action_target;
}
Need to get the latest merged operation on each action_target
Sample input data:
[add|1001|item1, add|1002|item2, delete|1003|item1, add|1004|item1]
Expected result:
[add|1002|item2, add|1004|item1]
Sample input data:
[add|1001|item1, add|1002|item2, delete|1003|item1]
Expected result:
[add|1002|item2]
Sample input data:
[delete|1001|item1, add|1002|item2, add|1003|item1]
Expected result:
[add|1002|item2, add|1003|item1]
Is this approachable using Java8 stream APIs? Thanks.
You want to group by one criteria (the action_target) combined with reducing the groups to the maximum of their action_time values:
Map<String,Item> map=items.stream().collect(
Collectors.groupingBy(item->item.action_target,
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(item->item.action_time)),
Optional::get)));
This returns a Map<String,Item> but, of course, you may call values() on it to get a collection of items.
Beautified with static imports, the code looks like:
Map<String,Item> map=items.stream().collect(groupingBy(item->item.action_target,
collectingAndThen(maxBy(comparing(item->item.action_time)), Optional::get)));
Your additional request of taking care of idempotent "add" and follow-up "delete" actions can be simplified to “remove items whose last action is "delete"” which can be implemented just by doing that after collecting using a mutable map:
HashMap<String,Item> map=items.stream().collect(groupingBy(
item->item.action_target, HashMap::new,
collectingAndThen(maxBy(comparing(item->item.action_time)), Optional::get)));
map.values().removeIf(item->item.action_name.equals("delete"));

Java 8 Streams : build multilevel / composite objects

I'm starting using java 8 stream API.
I would like to convert a list of "sql result set" to domain objects, i.e composite structure.
Domain objects : a user has a collection of permissions, each permission has a collection of year of applications.
For example, John has 2 permissions (MODERATOR and DEV).
its moderator permission is only applicable for 2014 and 2015
its dev permission of only applicable for 2014.
class User {
// some primitives attributes
List<Permission> permission;
}
class Permission {
// some primitives attributes
List<Integer> years;
}
Now I make a query and got a list of flat results, something like:
[1, "moderator", 2014]
[1, "moderator", 2015]
[1, "dev", 2014]
[2, "dev", 2010]
[2, "dev", 2011]
[2, "dev", 2012]
The 1 and 2 are userId.
I tried various construction but at the end it's more complex than fluent. And didn't work :)
I read in a Java 8 book that it's "simple" to build dompain objects with collectors.
I cried a little when I read that :'(
I tried
sb.collect(
collectingAndThen(
groupingBy(
Mybean::getUserId,
collectingAndThen(
groupingBy(Monbean::getPermissionId, mapping(convertPermission, toList())),
finisherFonction)
),
convertUser)
);
and got one hell of generics compilation failure.
what's the best way to construct multi level composite domain objects using java 8 streams ?
is collectionAndThen / finisher a good idea?
or do you use only groupingBy followed by a mapping function?
do you transform the classifier to an object (sort of first level mapping function?)
Because at the end I want to get rid of the Map and got a List<User> result (I think I can add a map call on the entrySet to finish the transformation).
Let me offer you a few options and you decide which looks most clear to you. I am assuming that User constructor is User(int userId, List<Permission> permissions) and Permission constructor is Permission(String permissionId, List<Integer> years)
Option 1: The direct approach. Group by userid, construct a list of permissions for each userid and make User objects. Personally, I find this much nesting in collectors to be hard to follow.
List<User> users = beans.stream()
.collect(
groupingBy(
MyBean::getUserid,
collectingAndThen(
groupingBy(
MyBean::getPermission,
mapping(MyBean::getYear, toList())
),
t -> t.entrySet().stream()
.map(e -> new Permission(e.getKey(), e.getValue()))
.collect(toList())
)
)
).entrySet().stream()
.map(e -> new User(e.getKey(), e.getValue()))
.collect(toList());
Option 2: Same as above but make the permission collector separately for clarity.
Collector<MyBean, ?, List<Permission>> collectPermissions = collectingAndThen(
groupingBy(MyBean::getPermission, mapping(MyBean::getYear, toList())),
t -> t.entrySet().stream()
.map(e -> new Permission(e.getKey(), e.getValue()))
.collect(toList())
);
List<User> users = beans.stream()
.collect(groupingBy(MyBean::getUserid, collectPermissions))
.entrySet().stream()
.map(e -> new User(e.getKey(), e.getValue()))
.collect(toList());
Option 3: First roll the beans into a map of userid to map of permissionid to list of years (Map<Integer, Map<String, List<Integer>>). Then construct the domain objects out of the map
List<User> users = beans.stream().collect(
groupingBy(
MyBean::getUserid,
groupingBy(
MyBean::getPermission,
mapping(MyBean::getYear, toList())
)
)
).entrySet().stream()
.map(u -> new User(
u.getKey(),
u.getValue().entrySet().stream()
.map(p -> new Permission(p.getKey(), p.getValue()))
.collect(toList())
)
).collect(toList());
The collectingAndThen combinator is designed for when the intermediate form for accumulation differs from the desired final form. This is the case for the joining() collector (where the intermediate form is a StringBuilder, but the final form is a String), and also can be used to wrap mutable collections with immutable wrappers after the collecting is done. So I don't think this is the tool you are looking for.
If you're looking for "permissions by user", this will do the trick:
Map<UserId, List<Permission>>
queryResults.stream()
.collect(qr -> qr.getId(),
mapping(qr -> qr.getPermission() + ":" + qr.getDate(),
toList()));
This would result in:
1 -> [ moderator:2014, moderator:2015, dev:2014 ]
2 -> [ dev:2010, dev:2011, dev:2012 ]
The idea is:
- You are grouping by "id" (the first field of your query)
- For each record, you select some function of the remaining fields (here, I used string concat for clarity, but you could create an object holding the permission and year), and send that to the downstream collector
- Use Collectors.toList() as the downstream collector, which will then be the "value" of the Map created by groupingBy.

getting a hashmap in R using rJava

I have a plain hashmap with numeric values and would like to retrieve its content, ideally in a list (but that can be worked out).
Can it be done?
Try this:
library(rJava)
.jinit()
# create a hash map
hm<-.jnew("java/util/HashMap")
# using jrcall instead of jcall, since jrcall uses reflection to get types
.jrcall(hm,"put","one", "1")
.jrcall(hm,"put","two","2")
.jrcall(hm,"put","three", "3")
# convert to R list
keySet<-.jrcall(hm,"keySet")
an_iter<-.jrcall(keySet,"iterator")
aList <- list()
while(.jrcall(an_iter,"hasNext")){
key <- .jrcall(an_iter,"next");
aList[[key]] <- .jrcall(hm,"get",key)
}
Note that using .jrcall is less efficient than .jcall. But for the life of me I can not get the method signature right with .jcall. I wonder if it has something to do with the lack of generics.
I have never done this myself, but there is an example in the rJava documentation of creating and working with a HashMap using the with function:
HashMap <- J("java.util.HashMap")
with( HashMap, new( SimpleEntry, "key", "value" ) )
with( HashMap, SimpleEntry )

Categories