To understand the map function in Streams better I was trying something like this:
String inputString="1+3+5";
Stream.of(inputString.split("\\+")).map(
eachStringLiteral -> {
output += mapOfStringAndNumber.get(eachStringLiteral) + literal;
}
);
Where inputString is:
String inputString = "1+3+5";
however, the compiler complains and I don't know why:
The method map(Function) in the type
Stream is not applicable for the arguments ((
eachStringLiteral) -> {})
I also need some help in order to understand the syntax in
Function<? super String,? extends R>.
Update
This is whole code illustrating what I am trying to achieve:
HashMap<String,Double> mapOfStringAndNumber=new HashMap<String,Double>();
mapOfStringAndNumber.put("1",270.5);
mapOfStringAndNumber.put("2",377.5);
mapOfStringAndNumber.put("3",377.5);
String inputString="1+3+5";
String literal="+";
String output;
java.util.stream.Stream.of(inputString.split("+")).map(eachStringLiteral->
output+=mapOfStringAndNumber.get(eachStringLiteral)+literal
Assuming that you provide a Double value to each input String in your map mapOfStringAndNumber, you could use Pattern#splitAsStream(CharSequence input) to split your input String directly as a Stream and use Collectors.joining(CharSequence delimiter) to build your output with + as delimiter, so your final code could be:
Map<String,Double> mapOfStringAndNumber = new HashMap<>();
mapOfStringAndNumber.put("1",270.5);
mapOfStringAndNumber.put("3",377.5);
mapOfStringAndNumber.put("5",377.5);
String inputString = "1+3+5";
String output = Pattern.compile("\\+")
.splitAsStream(inputString)
.map(mapOfStringAndNumber::get)
.map(d -> Double.toString(d))
.collect(Collectors.joining("+"));
System.out.println(output);
Output:
270.5+377.5+377.5
I have a couple of suggestions for your code:
If you use a {}-block and you want to create a Function (not a Runnable, Consumer or similar) you have to return something.
Unless you have a very special case, use Integer.parseInt(String) instead of a Map of Strings to Integers
You must not modify output from within a Stream. That could lead to concurrancy issues.
Do you really need literal?
My example code for your usecase would look like this:
String inputString="1+3+5";
return
// split input and create stream
Stream.of(inputString.split("\\+"))
// convert each item to Integer
.mapToInt(Integer::parseInt)
// sum up everything
.sum();
map, by definition, takes an input stream and applies a Function to each element of this stream, creating a new stream out of those modified elements.
So the count of items in input stream has to be the same as the count of items in output stream.
The error you are mentioning occurs, because your mapping function does not return anything: output+=mapOfStringAndNumber.get(eachStringLiteral)+literal only modifies the output variable and returns nothing.
From what I'm seeing here, you want to perform map and reduce operations:
map operation changes each element of input stream into its value from mapOfStringAndNumber and adding literal to it
reduce operation, in your case, is summing up all elements of your stream
To achieve this using Java 8 features, you need to first map the elements of your stream and then sum them up. You can do it like this:
String sum = Stream.of(inputString.split("\\+"))
.map(stringLiteral->
mapOfStringAndNumber.get(stringLiteral).toString())
//join all the strings in the stream with a provided delimiter
.collect(Collectors.joining(literal));
output+=mapOfStringAndNumber.get(eachStringLiteral)+literal; is an expression. Lambda expressions that have a body consisting of a single expression should not have them placed within curly braces:
Stream.of(inputString.split("+")).map(eachStringLiteral ->
output+=mapOfStringAndNumber.get(eachStringLiteral)+literal;
);
See the section of the Java Language Specification concerning lambda bodies:
A lambda body is either a single expression or a block (§14.2)...
LambdaBody:
Expression
Block
Alternatively you can just add a return clause before the output variable to make it a statement.
Related
I have a Collection of strings.
I need to replace some characters with given relevant characters.
I was tried this. But my code but doesn't work correctly.
public class MyPattern {
public static void main(String[] args)
{
String items;
Collection<String> arr = new ArrayList<String>();
arr.add("I00.30"); //Expected output: 100.30
arr.add("8B4.99"); //Expected output: 884.99
arr.add("B37.2B"); //Expected output: 837.28
arr.add("292.b4"); //Expected output: 262.64
arr.add("24|.7O"); //Expected output: 241.70
arr.add("B55.I4"); //Expected output: 855.14
arr.add("444.07"); //Expected output: 444.07
for(String item:arr)
{
System.out.println(item);
items = item.toUpperCase().replaceAll("l", "1")
.replaceAll("l", "1")
.replaceAll("i", "1")
.replaceAll("|", "1")
.replaceAll("G", "6")
.replaceAll("b", "6")
.replaceAll("B", "8")
.replaceAll("O", "0");
System.out.println(items);
}
}
}
The letters passed in the replaceAll() method has to be replaced in every item in the collection.
Is there any way to find the irrelevant character and replace it with the number (as shown in the code above) ?
Your code is resigning the variable item, it will not affect the list contents.
To be able to do that, you might change the type of variable arr to List. With that, you can iterate over it by using the traditional (index based) for loop.
List<String> arr = // initializing the list
for (int i = 0; i < arr.size(); i++) {
String item = replace(arr.get(i)); // method replace provided below
arr.set(i, item);
}
Another option is to use Java 8 replaceAll() method, which expects a function that will be applied to every element in the collection.
arr.replaceAll(str -> replace(str));
public static String replace(String source) {
return source.toUpperCase()
.replaceAll("[LI|]", "1")
.replaceAll("[GB]", "6")
.replace("O", "0");
}
Note that method replaceAll() that expect a regular expression is more expensive than replace(). Hence, when you don't need a regex, its better to use any flavor of replace() (with either char or String arguments).
Here you can benefit from replaceAll() by processing characters L, L and | in one go with a regular expression "[LI|]".
For more information on regular expressions take a look at this tutorial
There's also a minor issue in your code:
After toUpperCase() has been applied, it doesn't make sense to try to replace lowercase letters like 'l' or 'i'.
There's a clash "b", "6" and "B", "8".
I hope with all these hints you'll be able to manage to get it working.
Instead of using the for loop. You can use, stream.
For example, if you want to change i to 1 in every element, you can get it using the following code. And you want to
Collection<String> arr_new = arr.stream().map(String::toLowerCase).map(t->t.replaceAll("i",1)).map(String::toUpperCase);
Here, the first map converts the String to lower-case, the second map do replace what you need and the third map convert it to the upper case.
I hope this is what you are looking for.
I am trying to get the number after doing calculations using Lambda expression, but getting the error.
The lambda expression I am using:
int num = Optional.ofNullable(list.stream().filter(x->x.getType().getTypeId()==Type.getTypeId()).limit(1).map(x->x.getNum())).get();
After filtering, I want to get the first retrieved value. But I am getting the error as
cannot convert from Stream<Integer> to int
So, currently the way I am using is
Optional<> li = list.stream().filter(x->x.getType().getTypeId()==Type.getTypeId()).findFirst();
if (li.isPresent()) {
num = li.map(x-> x.getNum()).get();
}
But, I was looking if the above could be done in a single line rather than extra if statement
Earlier, I tried the get() with findFirst(), but it was giving nullpointerException. How can I safely retrieve the value.
list.stream().filter(x->x.getType().getTypeId()==Type.getTypeId()).limit(1).map(x->x.getNum()) returns a Stream. You are lacking the findFirst terminal operation:
int num =
list.stream()
.filter(x->x.getType().getTypeId()==Type.getTypeId())
.map(x->x.getNum())
.findFirst()
.orElse(0); // default value in case the Stream is empty after the filtering
Hey guys I have been having trouble removing duplicate letters from a string. I have tried for loops using string methods but I dont seen to be getting anywhere. Does anyone know how to either remove duplicate letters from either a linked list or a string. I have a String sent which holds a a a c d e e k o r t. I also have a LinkedList word = new Linked List(); which has the same letters.
By utilizing the properties of a LinkedHashSet, which guarantees uniqueness among its elements and preserves order:
String string = "foobar";
Set<Character> uniqueChars = new LinkedHashSet<>();
for (char character : string.toCharArray()) {
uniqueChars.add(character );
}
uniqueChars.forEach(character -> System.out.print(character)); // fobar
A more advanced alternative is to make use of Java 8 Stream API, which will accomplish the same:
String string = "foobar";
LinkedHashSet<Character> uniqueChars = string.chars()
.mapToObj(character -> (char) character )
.collect(Collectors.toCollection(LinkedHashSet::new));
uniqueChars.forEach(character -> System.out.print(character)); //fobar
there are 2-3 ways to achieve the same like below:
Try to put value in SET
new HashSet<>(listWithDuplicates)
The same can be done using Guava as well:
Lists.newArrayList(Sets.newHashSet(listWithDuplicates));
Finally – let’s look at a new solution, using Lambdas in Java 8; we’re going to use the distinct() method from the Stream API which returns a stream consisting of distinct elements based on the result returned by equals() method:
listWithDuplicates.stream()
.distinct()
.collect(Collectors.toList());
Please let me know if it helps..
for Stream api details and example please follow below URL:
https://kpblogs.com/java/java8/java-stream-api-with-example/
I have a loop which update an String object:
String result = "";
for (SomeObject obj: someObjectList) {
result = someMetohd(obj, result);
}
An implementation of someMethod is irrelevant:
private String someMethod(SomeObject obj, String result) {
result = result.concat(obj.toString());
return result;
}
And I want to use Stream instead a loop. How to implement it with Stream?
#SuppressWarnings("OptionalGetWithoutIsPresent")
String result = Stream.concat(Stream.of(""), someObjectList.stream())
.reduce(this::someMethod)
.get();
Your someMethod should be associative as specified in the documentation, however this is only important for parallel streams, while your code is explicitly sequential
As you always add to the result, you can consider it a first element of the stream and then use reduce method which will always merge first two elements - current result and next element
result has to be the first parameter of your someMethod
Because all elements in the stream have to be of the same type, while you have String result and SomeObject elements, you need to change the signature of someMethod to accept two Objects (and do the casts inside the method): private String someMethod(Object result, Object obj). This is the most ugly part of this solution.
You can inline the initial value of the result - no need to define result upfront
You might want to change this::someMethod depending on where this method is declared
Finally, you don't need to worry about handling Optional result, because the stream always has at least one element so it's safe to just call get()
final StringBuilder resultBuilder = new StringBuilder();
someObjectList.stream().map(SomeObject::toString).forEach(resultBuilder::append);
final String result = resultBuilder.toString();
To know more about Streams, you can check this page: http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/, I think it's very helpful.
Although the functional equivalent of what you're trying to achieve here is possible with streams, it's worth reminding you that functional and iterative ways of thinking are not necessarily compatible.
Generally you think of each element on its own, and you don't have visibility over other elements, unless you're using a special function like reduce.
Here's something that does what you've asked for:
final List<Object> objectList = Arrays.asList("a", "b", "c", "d");
String concatString = objectList.stream()
.map(e -> e.toString())
.reduce((result, element) -> result.concat(e))
.get();
Map turns the entire stream into a list, but with the toString function called separately on every element. Reduce is more complex. It can be described as an accumulation. It executes a function between the result, and the current element. In this case, it takes the first element, and concatenates it to the second. It then takes the first/second concatenation, and applies the same function to the third. And so on.
Instead of dealing with lambdas, you can also pass in methods directly, to tighten up your code a bit:
String result = objectList.stream()
.map(Object::toString)
.reduce(String::concat)
.get();
For Example
a set{1,2,3,4,5} with a delimiter #
I want to have a return string of 1#2#3#4#5
The tricky part is, I have many different sets,
Set<Integer>, Set<String>, Set<Long> and so on.
And I know I can do it in separate functions using a loop. But I just don't want to be tedious and want to do it in one function using Java Generics or whatever that can help. I have done some brain storm but in vain. :(
Any idea is appreciated!
You can use the join method of StringUtils class of the Commons Lang library
Example:
Set<Integer> theSet = new HashSet<Integer>();
theSet.add(1);
theSet.add(2);
theSet.add(3);
theSet.add(4);
theSet.add(5);
System.out.println(StringUtils.join(theSet,'#'));
Output: 1#2#3#4#5
Edit: the answer from mabbas is a better idea than this.
It's not particularly great, but will work assuming that your Set contains objects whose toString representations you want to concatenate
public String concatenateSet(Set<?> set) {
return set.stream()
.map(Object::toString)
.reduce((previous, current) -> previous + "#" + current)
.orElse("");
}
Step by step:
Calling stream() gets you a stream of items in the set
Calling map(Object::toString) calls toString on each item of the set
Calling reduce((previous, current) -> previous + "#" + current) concatenates the toString representations of each object with # as a delimiter between each item
Calling orElse("") will ensure that an empty sting is returned if, for example, the set is empty
You're over thinking it. You can just take a Set as a parameter and it will cover every type of set. Just make sure your objects implement toString().
public static String setToString(Set set)
{
Iterator it = set.iterator();
StringBuffer out = new StringBuffer(it.next().toString());
while (it.hasNext())
{
out.append("#" + it.next());
}
return out.toString();
}