Joining Strings via stream - java-8

I am trying to join a list of names:
List<String> names;
names = books.stream()
.map( b -> b.getName() )
.filter( n -> ( (n != null) && (!n.isEmpty()) ) )
.collect(Collectors.joining(", "));
This does not compile saying:
incompatible types. inference variable R has incompatible bounds
So after some research, it seems that there is something I misunderstood. I thought that .map( b -> b.getName() ) returned/changed the type to a String, and it seems something is wrong there. If I use .map(Book::getName) instead, I still get an error, but I probably don't fully understand the difference.
However, this does not complain:
List<String> names;
names = books.stream()
.map( b -> b.getName() )
.map( Book::getName )
.filter( n -> ( (n != null) && (!n.isEmpty()) ) )
.collect(Collectors.joining(", "));
Can someone explain me why? Some didactic explanation about differences between .map( b -> b.getName() ) and .map(Book::getName) are appreciated too, since I think I didn't get it right.

The joining(", ") collector will collect and join all Strings into a single string using the given separator. The returning type of collect in this case is String, but you are trying to assign the result to a List. If you want to collect Strings into a List, use Collectors.toList().
If you have a collection with Book instances, then it will be enough to map a stream of Books to a stream of Strings once.
Difference between lamdba & method refrence
A lamdba expression may be written as a block, containing multiple operations:
b -> {
// here you can have other operations
return b.getName();
}
if lambda has single operation, it can be shortened:
b -> b.getName()
Method reference is just a "shortcut" for a lambda with a single operation. This way:
b -> b.getName()
can be replaced with:
Book::getName
but if you have a lambda like this:
b -> b.getName().toLowerCase()
you cant use a reference to the getName method, because you are making and additional call to toLowerCase().

If you are using Collectors.joining(), the result will be a single concatenated String:
String names = books.stream()
.map( b -> b.getName() )
.filter(n -> (n != null) && !n.isEmpty())
.collect(Collectors.joining(", "));
The Collectors.toList() is the one that returns a List:
List<String> namesList = books.stream()
.map( b -> b.getName() )
.filter(n -> (n != null) && !n.isEmpty())
.collect(Collectors.toList());
Book::getName is a method reference and will have the same result as b -> b.getName(). Method reference is clearer and enables to pass other existing methods as a parameter to methods such as map(), as long as the passed method conforms to the signature of the expected functional interface. In this case, map() expects an instance of the Function interface. Thus, you can give any reference to a method that conforms to the signature of the abstract R apply(T t) method from such an interface.
Since you are mapping a Book to a String, the actual signature for the method to be given to the map() must be String apply(Book t). This can be read as "receive a Book and return a String". This way, any method you pass that conforms to this definition is valid. When you pass a method reference Book::getName, the getName method itself doesn't conform to the signature presented above (because it has no parameter at all), but it conforms to the definition of such a signature: you pass a book and return a String from its name.
Thus, consider that, inside the class where you have your book list, you also have a method which performs any operation over a Book, returning a String. The method below is an example that receives a Book and gets the first 10 characters from its name:
public String getReducedBookName(Book b){
if(b.getName() == null)
return "";
String name = b.getName();
return name.substring(0, name.length() > 10 ? 10 : name.length());
}
You can also pass this method (which is not inside the Book class) as parameter to the map() method:
String names = books.stream()
.map(this::getReducedBookName)
.filter(n -> !n.isEmpty())
.collect(Collectors.joining(", "));

if you prefer mapping over map
as String
String names = books.stream().collect(mapping(Book::getName,
filtering(s -> s != null && ! s.isBlank(),
joining(", "))));
as List
List<String> names = books.stream().collect(mapping(Book::getName,
filtering(s -> s != null && ! s.isBlank(),
toList())));

Related

Avoid counting values of Ints with for loop in Kotlin

I have a list of A class objects
data class A{
val abc: Abc
val values: Int?
}
val list = List<A>
If I want to count how many objects I have in list I use:
val count= a.count()
or val count= a.count(it -> {})
How to append all values in the list of objects A avoiding for loop? Generaly Im looking for proper kotlin syntax with avoiding code below
if (a!= null) {
for (i in list) {
counter += i.values!!
}
}
Either use sumBy or sum in case you have a list of non-nullable numbers already available, i.e.:
val counter = list.sumBy { it.values ?: 0 }
// or
val counter = extractedNonNullValues.sum()
The latter only makes sense if you already mapped your A.values before to a list of non-nullable values, e.g. something like:
val extractedNonNullValues= list.mapNotNull { it.values } // set somewhere else before because you needed it...
If you do not need such an intermediate extractedNonNullValues-list then just go for the sumBy-variant.
I don't see you doing any appending to a list in the question. Based on your for loop I believe what you meant was "How do I sum properties of objects in my list". If that's the case you can use sumBy, the extension function on list that takes a labmda: ((T) -> Int) and returns an Int like so:
val sum = list.sumBy { a -> a.values ?: 0 }
Also, calling an Int property values is pretty confusing, I think it should be called value. The plural indicates a list...
On another note, there is a possible NPE in your original for loop. Avoid using !! on nullable values as, if the value is null, you will get an NPE. Instead, use null coalescing (aka elvis) operator to fall back to a default value ?: - this is perfectly acceptable in a sum function. If the iteration is not to do with summing, you may need to handle the null case differently.

How to replace a value in one map with the value of another map, based on a pattern match?

I have a map that has a few key value pairs, and I would like a way to go through those pairs and attempt to match the keys with the value of another map. If there's a match the values are substituted for each other. In other words, if there's a match value of the second map is substituted for the value of the first map. If there is no match, it is not included in the result.
I've tried figuring out the logic use the scala .map function but I'm new to scala and can't quite figure it out.
For example, I have the following two scala Map[String, String]:
val lookupMap = Map("aaa" -> "apple", "bbb" -> "orange", "ccc" -> "banana")
val entriesMap = Map("foo" -> "ccc", "bar"-> "aaa", "baz" -> "zzz")
I would like some way to get the following result:
val result = Map("foo" -> "banana", "bar" -> "apple")
Note: "baz" was not included because it did not match to anything in the lookup Map.
A for comprehension can clean that up.
val result = for {
(k,ev) <- entriesMap
lv <- lookupMap.get(ev)
} yield (k,lv)
//result: Map[String,String] = Map(foo -> banana, bar -> apple)
Lets break down your problem in simpler steps.
Filter out all pairs on the entriesMap whose value does not exists as a key in the lookupMap.
Map the remaining pairs to change the value for the value on the lookupMap associated with the original value.
Thus, you can write the following:
val result =
entriesMap
.filter { case (_, value) => lookupMap.contains(key = value) }
.map { case (key, value) => key -> lookupMap(value) }
However, every time that you want to filter and then map, you can always use collect (which will do the same job, but in just one iteration).
Thus, you can write this:
val result = entriesMap.collect {
case (key, value) if lookupMap.contains(key = value) => key -> lookupMap(value)
}
Now, one "problem" with the above code is that it uses the unsafe apply over a Map, which will throw an exception if they key does not exists.
Usually, one should use the get method, which would return the value wrapped on an Option, which will be a None if the key did not existed.
In this case, the access is not unsafe, because we are checking if the key exists before.
Anyways, one could rethink the program as:
Map the values of the entriesMap by attempting to get their associated value on the lookupMap.
Filter out the pairs where its values is now a None and unwrapping the Somes.
The code will be as follows:
val result =
entriesMap
.view // Only if you are in 2.13
.mapValues(value => lookupMap.get(key = value))
.collect { case (key, Some(value)) => key -> value }
.toMap // This is necessary because mapValues returns a view, which is a lazy collection.
// Other option would have been to use just map instead of mapValues.
Finally, instead of using higher order functions directly, one could use for comprehension.
Thus, this code (almost the same as the one from jwvh's answer):
val result =
for {
(key, value) <- entriesMap // For each key-value pair in entriesMap...
newValue <- lookupMap.get(key = value) // And for each newValue associated with each original value...
} yield key -> newValue // Yield the key-newValue pair.

Get the maximum value using stream for Map

I have a class called Test. This class has a method called getNumber which returns an int value.
public class Test{
.
.
.
.
public int getNumber(){
return number;
}
}
Also I have a HashMap which the key is a Long and the value is a Test object.
Map<Long, Test> map = new HashMap<Long, Test>();
I want to print the key and also getNumber which has a maximum getNumber using a Stream Line code.
I can print the maximum Number via below lines
final Comparator<Test> comp = (p1, p2) -> Integer.compare(p1.getNumber(), p2.getNumber());
map.entrySet().stream().map(m -> m.getValue())
.max(comp).ifPresent(d -> System.out.println(d.getNumber()));
However my question is How can I return the key of the maximum amount? Can I do it with one round using stream?
If I understood you correctly:
Entry<Long, Test> entry = map.entrySet()
.stream()
.max(Map.Entry.comparingByValue(Comparator.comparingInt(Test::getNumber)))
.get();
If you want to find the key-value pair corresponding to the maximum 'number' value in the Test instances, you can use Collections.max() combined with a comparator that compares the entries with this criteria.
import static java.util.Comparator.comparingInt;
...
Map.Entry<Long, Test> maxEntry =
Collections.max(map.entrySet(), comparingInt(e -> e.getValue().getNumber()))
If you want to use the stream way, then remove the mapping (because you lost the key associated with the value), and provide the same comparator:
map.entrySet()
.stream()
.max(comparingInt(e -> e.getValue().getNumber()))
.ifPresent(System.out::println);
Note that there is a small difference in both snippets, as the first one will throw a NoSuchElementException if the provided map is empty.

Additional brackets in Java8 stream map() function

I'm working/testing streams in Java8 and come across very frustrating issue.
I've got the code which compiles well:
List<String> words = Arrays.asList("Oracle", "Java", "Magazine");
List<String> wordLengths = words.stream().map((x) -> x.toUpperCase())
.collect(Collectors.toList());
And second one (nearly the same) which throw a warnings:
List<String> words = Arrays.asList("Oracle", "Java", "Magazine");
List<String> wordLengths = words.stream().map((x) -> {
x.toUpperCase();
}).collect(Collectors.toList());
Warning:
The method map(Function<? super String,? extends R>) in the type Stream<String> is not applicable for the arguments ((<no type> x) -> {})
What does this additional brackets have changed?
Your lambda expression returns a value. If you use brackets you need to add a return statement to your lambda function:
List<String> words = Arrays.asList("Oracle", "Java", "Magazine");
List<String> wordLengths = words.stream().map((x) -> {
return x.toUpperCase();
}).collect(Collectors.toList());
According to the official Oracle tutorial
A lambda expression consists of the following:
A comma-separated list of formal parameters enclosed in parentheses.
The CheckPerson.test method contains one parameter, p, which
represents an instance of the Person class.
Note: You can omit the data type of the parameters in a lambda
expression. In addition, you can omit the parentheses if there is only
one parameter. For example, the following lambda expression is also
valid:
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
The arrow token, ->
A body, which consists of a single expression or a statement block.
This example uses the following expression:
p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
If you specify a single expression, then the Java runtime evaluates
the expression and then returns its value. Alternatively, you can use
a return statement:
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
A return statement is not an expression; in a lambda expression, you
must enclose statements in braces ({}). However, you do not have to
enclose a void method invocation in braces. For example, the following
is a valid lambda expression:
email -> System.out.println(email)
Since there is only one parameter in the provided lambda expression (x) -> x.toUpperCase() we can omit the parentheses: x -> x.toUpperCase(). String#toUpperCase returns a new String so there is no need to use return statement and braces. If instead we had a complex block with return statements we would have to enclose it into braces. Moreover in this case it is better to use Method Reference String::toUpperCase
List<String> wordLengths = words.stream().map(String::toUpperCase).collect(Collectors.toList());

multiline boolean expression in haskell

I have a function findEntryByAddress which has an inline declararation of filterEntry. I would like to add another boolean check, but then there would be too many chars in one line.
How can I split the boolean expression, one equals check per line.
findEntryByAddress :: Address -> AddressBook -> Maybe Entry
findEntryByAddress = head <<< filter filterEntry
where
filterEntry :: Entry -> Boolean
filterEntry entry = entry.firstName == firstName && entry.lastName == lastName
I have checked Haskell's Indentation guide, nothing on multiline expressions.
code from purescript-book.
I'm not sure I understand the question, you want to do something like this?
findEntryByAddress :: Address -> AddressBook -> Maybe Entry
findEntryByAddress = head <<< filter filterEntry
where
filterEntry :: Entry -> Boolean
filterEntry entry = entry.firstName == firstName &&
entry.lastName == lastName &&
entry.age == age
In a where, you can jump a line but you have to put the rest of the expression one space after the beginning of the definition otherwise Haskell will think you're defining something new. So this is correct:
where f = x &&
y
But this isn't:
where f = x &&
y
By the way, in Haskell, there is no Boolean type, the type of True and False is Bool.

Resources