I have a case where A has a list of B and B has some property i need.
pseudocode structure
class A
List<B> elements;
class B
Property property;
I have List of A. In order, to get property i should go through the double foreach loop to get the property i want. Like this:
String myFutureProp = null;
for (A a: list of A) {
for(B b: list of B) {
if("MY_PROPERTY".equals(b.getKey) {
myFutureProp = b.getValue();
}
}
}
I was thinking to get it more tasty using Stream API. I was looking forward with
forEach() solution:
final String[] myFutureProp = {null}
a.getElements()
.foreach(b -> b.getElements().stream()
.filter("MY_PROPERTY"::equals)
.forEach(prop -> myFutureProp[0] = (String)prop.getValue);
Then i'm taking myFutureProp[0] but it looks ugly to be honest. Is it any another solution in Stream API that i'm able to use?
I hope I got your class structure right.
You can use flatMap to get a Stream of all the Propertys of all the B instances, and return any property having the required key (if found) :
Optional<String> myFutureProp =
aList.stream()
.flatMap(a->a.getElements().stream().map(B::getProperty))
.filter(prop->prop.getKey().equals("MY_PROPERTY"))
.map(Property::getValue)
.findAny();
This should do a work -> get last matching value
final String value = bList.stream()
.flatMap(bElement -> bElement.elements().stream())
.map(aElement -> aElement.getProperty())
.filter(property -> property.getKey().equals("MY_PROPERTY"))
.map(property -> property.getValue())
.reduce((a,b) -> b).orElse(null);
Related
So, I have sorted by condition list of objects
private Observable<CallServiceCode> getUnansweredQuestionList() {
return Observable.fromIterable(getServiceCodeArrayList())
.subscribeOn(Schedulers.computation())
.filter(iServiceCode -> iServiceCode.getServiceCodeFormStatus().isUnanswered());
}
and now what I need to do:
Every object has list servicePartList , I need to filter this list by condition and eventually if final size of this filtered list >0, so I need to add object that contains this list CallServiceCode object as a key and this filtered list as a value.
So it should be like this:
private Map<CallServiceCode, ArrayList<CallServicePart>> getSortedMap() {
Map<CallServiceCode, ArrayList<CallServicePart>> result = new HashMap<>();
getUnansweredQuestionList()
.filter(callServiceCode -> Observable.fromIterable(callServiceCode.getCallServicePartList()) //
.filter(servicePart -> servicePart.getServicePartFormStatus().isUnanswered())//
.isNotEmpty())
.subscribe(callServiceCode -> result.put(callServiceCode, Observable.fromIterable(callServiceCode.getCallServicePartList()) //
.filter(servicePart -> servicePart.getServicePartFormStatus().isUnanswered()));
return result;
}
But there is no such method isNotEmpty() in RxJava2 and also it is not right to add key like this:
Observable.fromIterable(callServiceCode.getCallServicePartList())
.filter(servicePart -> servicePart.getServicePartFormStatus().isUnanswered())
So question is how to make it properly?
One solution could be to use collect to create the Map directly from the observable:
return getUnansweredQuestionList()
.collect(HashMap<CallServiceCode, List<CallServicePart>>::new,(hashMap, callServiceCode) -> {
List<CallServicePart> callServiceParts = Observable.fromIterable(callServiceCode.getServicePartList())
.filter(s -> !s.getServicePartFormStatus().isUnanswered())
.toList().blockingGet();
if (!callServiceParts.isEmpty())
hashMap.put(callServiceCode, callServiceParts);
}).blockingGet();
If you extract filtering into a method (could be also member of CallServiceCode) then the code is much cleaner:
return getUnansweredQuestionList()
.collect(HashMap<CallServiceCode, List<CallServicePart>>::new, (hashMap, callServiceCode) -> {
List<CallServicePart> filteredParts = getFilteredServiceParts(callServiceCode.getServicePartList());
if (!filteredParts .isEmpty())
hashMap.put(callServiceCode, filteredParts);
}).blockingGet();
I have been trying to translate this
var winner =
node.Connections.Where(n => n.HQ != null).GroupBy(n
=>n.HQ)
.Select(g => new { Cantidate = g.Key, Count =
g.Count() })
.OrderByDescending(g => g.Count)
.First()
.Cantidate;
to Java, although I am not sure I can achieve this with streams. I would like for someone to explain to me exactly what that code does or to help me translate this to Java.
I have been looking up to this resource: https://github.com/mythz/java-linq-examples/blob/master/README.md
but I still cannot grasp what those lines of code do.
I understand first 3 lines, but the select gets me lost.
Thanks in advance
EDIT:
After trying some code from help here, I got this:
Map<Candidate,List<Candidate>> winnersByWinner = node.getConnections().stream()
.map(Node::getHQ)
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(Function.identity()));
winner = winnersByWinner.entrySet().stream()
.map(e -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), e.getValue().size()))
.sorted(new Comparator<AbstractMap.SimpleImmutableEntry<Candidate, Integer>>() {
#Override
public int compare(AbstractMap.SimpleImmutableEntry<Candidate, Integer> o1, AbstractMap.SimpleImmutableEntry<Candidate, Integer> o2) {
Integer o1v = o1.getValue();
Integer o2v = o2.getValue();
if(o1v==o2v) {
Integer o1k = o1.getKey().getId();
Integer o2k = o2.getKey().getId();
return o2k.compareTo(o1k);
}
return o1v.compareTo(o2v);
}
})
//.reversed())
//.sorted(Comparator.comparingInt(AbstractMap.SimpleImmutableEntry::getValue).reversed())
.map(Map.Entry::getKey)
.findFirst()
.orElseGet(null);
Thanks to Novaterata.
This:
candidates.Select(c => nodes.Count(n => n.HQ == c));
translates to:
candidates.stream().map(c -> nodes.stream().map(Node::getHQ).filter(c::equals).count())
Thanks to Novaterata again.
My code works now quite ok, but I have to edit to make sure of one last thing that I may have translated badly:
nodes.Where(n => n.Committes.Any()
translated to:
nodes.stream().filter(n -> !n.Committes.isEmpty()).collect(Collectors.toList())
Is this correct?
This is assuming node is a type Node with a getHQ() property. This is my approximation of what you'd need to do. Using SimpleImmutableEntry inplace of the anonymous type. I'd create a simple private class to replace that for readability. This gist is that groupBy will result in a Map that you you need to turn back into a stream with .entrySet().stream() Map.Entry is very similar to KeyValuePair in C#. Hopefully the rest is self-explanatory. Any method or Class I've used here, you should look it up.
Map<HQ,List<HQ>> winnersByWinner = node.getConnection().stream()
.map(Node::getHQ)
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(Function.identity()))
HQ winner = winndersByWinnder.entrySet().stream()
.map(e -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), e.getValue().size()))
.sorted(Comparator.comparingInt(Map.Entry::getValue).reversed())
.map(Map.Entry::getKey)
.findFirst()
.orElseGet(null);
Following is the explanation of the code.
var winner =node.Connections.Where(n => n.HQ != null) // this is checking the connection where HQ is not null
.GroupBy(n=>n.HQ) // grouping based on HQ
.Select(g => new { Cantidate = g.Key, Count = g.Count() }) // creating an enumerable anonymous type with cantidate , count
.OrderByDescending(g => g.Count) // sorting this enumerable type in type order based on the count
.First() // take the first element from this enumerable type
.Cantidate; // get the value of cantidate of that first element
This will be the equivalent SQL
(from n in connection
where( n.HQ != null)
GroupBy(n.HQ) into g
select new { Cantidate = g.key, Count = g.Count()}
orderby Count descending).First().Cantidate
Use the link which You have provide to convert it into java.
I have a class
class ColumnTags {
String Name;
Collection<String> columnSemanticTags;
// constructor and getter and setters and other relevant attributes
}
I want to get the columnSemanticTags from a list of ColumnTags for a given name.
The corresponding method is as follows
public Collection<String> getTags(String colName, List<ColumnTags> colList)
{
Collection<String> tags = new ArrayList();
for(ColumnTag col:colList){
if(colName.equals(col.getName())){
tags = col.getColumnSemanticTags();
break;
}
}
return tags;
}
Want to convert the for loop to a java stream . I have tried
tags = colList.stream().filter(col -> colName.equals(col.getName()))
.map(col -> col.getColumnSemanticTags())
.collect(Collectors.toCollection());
I am getting compilation error. I am not aware what should be the Supplier . Have tried ArrayList::new . I have also tried casting it to ArrayList , but no success.
Can someone advice me what am I assuming wrong or what should be the expected way to handle this scenario.
With the solution , can someone explain as to why .collect() is a wrong way of tackling this solution.
public Collection<String> getTags(String colName, List<ColumnTags> colList) {
return colList.stream().filter(col -> colName.equals(col.getName()))
.map(col -> col.getColumnSemanticTags())
.findFirst().orElse(new ArrayList<>());
}
An easier way of going about this would be to simply filter a Stream to find exactly what you're looking for. If it is found, then return it, otherwise return an empty ArrayList:
return colList.stream()
.filter(c -> colName.equals(c.getName()))
.map(ColumnTag::getColumnSemanticTags)
.findFirst()
.orElseGet(ArrayList::new);
If you really want to use collect, you must call flatMap. That merges all of the lists (which are come from map(col -> col.getColumnSemanticTags())) into a single stream which contains all of the items.
List<String> tags = colList.stream()
.filter(col -> colName.equals(col.getName()))
.map(col -> col.getColumnSemanticTags())
.flatMap(collection -> collection.stream())
.collect(Collectors.toList());
I have an entity Employee
class Employee{
private String name;
private String addr;
private String sal;
}
Now i have list of these employees. I want to filter out those objects which has name = null and set addr = 'A'. I was able to achieve like below :
List<Employee> list2= list.stream()
.filter(l -> l.getName() != null)
.peek(l -> l.setAddr("A"))
.collect(Collectors.toList());
Now list2 will have all those employees whose name is not null and then set addr as A for those employees.
What i also want to find is those employees which are filtered( name == null) and save them in DB.One way i achieved is like below :
List<Employee> list2= list.stream()
.filter(l -> filter(l))
.peek(l -> l.setAddr("A"))
.collect(Collectors.toList());
private static boolean filter(Employee l){
boolean j = l.getName() != null;
if(!j)
// save in db
return j;
}
1) Is this the right way?
2) Can we do this directly in lambda expression instead of writing separate method?
Generally, you should not use side effect in behavioral parameters. See the sections “Stateless behaviors” and “Side-effects” of the package documentation. Also, it’s not recommended to use peek for non-debugging purposes, see “In Java streams is peek really only for debugging?”
There’s not much advantage in trying to squeeze all these different operations into a single Stream pipeline. Consider the clean alternative:
Map<Boolean,List<Employee>> m = list.stream()
.collect(Collectors.partitioningBy(l -> l.getName() != null));
m.get(false).forEach(l -> {
// save in db
});
List<Employee> list2 = m.get(true);
list2.forEach(l -> l.setAddr("A"));
Regarding your second question, a lambda expression allows almost everything, a method does. The differences are on the declaration, i.e. you can’t declare additional type parameters nor annotate the return type. Still, you should avoid writing too much code into a lambda expression, as, of course, you can’t create test cases directly calling that code. But that’s a matter of programming style, not a technical limitation.
If you are okay in using peek for implementing your logic (though it is not recommended unless for learning), you can do the following:
List<Employee> list2= list.stream()
.peek(l -> { // add this peek to do persistence
if(l.getName()==null){
persistInDB(l);
}
}).filter(l -> l.getName() != null)
.peek(l -> l.setAddr("A"))
.collect(Collectors.toList());
You can also do something like this:
List<Employee> list2 = list.stream()
.filter(l->{
boolean condition = l.getName()!=null;
if(condition){
l.setAddr("A");
} else {
persistInDB(l);
}
return condition;
})
.collect(Collectors.toList());
Hope this helps!
I have a catalog-like object hierarchy where every object has a name field.
class A {
List<A> list;
String name;
}
A{A{A{A...}AA},A{AAA},A{AAA}} // the depth is finite (~4)
I would like to provide a set of methods that return a list of child names (a a.getName()) of any parent element for a given name.
So for level 1 I have
a.getAs().stream().map(a1 -> a1.getName()).collect(Collectors.toList());
Level 2 I have already troubles with:
a1.getAs().stream().filter(a2 -> a2.getName() == name)
now I want to access the As and map them to their names but I don't know how
EDIT:
I have just realized that from the third level on it wouldn't be possible to find the list with just providing a single name. I would need a name for each level to be able to navigate to the node where the child list could be collected.
On one hand I could keep all the objects in one Set and access them with an id. They would still have references to each other. On the other hand by not knowing the root element I couldn't get the structure right.
I think I have to rethink the problem.
You can do it like this:
public static List<String> getChildNames(A node, String... path) {
Stream<A> s = node.getAs().stream();
for(String name: path)
s = s.filter(a -> a.getName().equals(name)).flatMap(a -> a.getAs().stream());
return s.map(A::getName).collect(Collectors.toList());
}
but if the names beneath an A node are unique, you should consider maintaining a Map<String,A>, mapping from child name to actual child, instead of a List<A>. That would make traversing a path via unique name/ID as simple as node.get(name1).get(name2). The logic of the method above would still be useful if you incorporate pattern matching, which doesn’t need to have a unique result.
public static List<String> getChildNames(A node, String... pathPatterns) {
Stream<A> s = node.getAs().stream();
for(String namePattern: pathPatterns) {
Pattern compiledPattern = Pattern.compile(namePattern);
s = s.filter( a -> compiledPattern.matcher(a.getName()).find())
.flatMap(a -> a.getAs().stream());
}
return s.map(A::getName).collect(Collectors.toList());
}
It works only for one level of the hierarchy:
public List<A> getSubcategoriesByParentName(A category, String name) {
return category.getSubcategories()
.stream()
.filter(subcategory -> subcategory.getName().equals(name))
.collect(Collectors.toList());
}
To achieve the next level, you could use a flatMap:
category.getSubcategories().stream()
.flatMap(s -> s.getSubcategories().stream())
.filter(s -> s.getName().equals(name))
.collect(Collectors.toList());
As you can see, there is a need of recursion, it is not a work for Stream API.
Of course, being aware of the depth, we could access to all levels (by using a flatMap(s -> s.getSubcategories().stream()) several times), but it will look ugly.