How to use If statements in a stream? - java

I have to write a method that gets an Integer List and converts it into a String by adding "e" in front a number if it is even or "u" if it is uneven and then seperates them by ",".
Example: Input:List with numbers 1,2,4,3-->Output:"u1,e2,e4,u3".
The Problem is that I have to do it using lambda expressions and streams and I dont know how to differentiate between even and uneven numbers with them.
If I do it like this i can only do it for even or uneven Numbers:
public static String getString(List<Integer> list){
String s = list.stream()
.map(Object::toString)
.filter(i->Integer.parseInt(i)%2==0)
.map(i->"g"+i)
.collect(Collectors.joining(","));
return s;
}
How can i use something like an If statement in a stream to decide whether to put "e" or "u" in front of the number?

You can put the condition in the map operation. The easiest way is using the ternary condition operator.
BTW, there's no reason to convert the Integer to a String and then parse it again to an int.
public static String getString(List<Integer> list){
return list.stream()
.map(i->i%2==0?"e"+i:"u"+i)
.collect(Collectors.joining(","));
}
EDIT: you can also use an if-else statement, but that would look less elegant:
public static String getString(List<Integer> list){
return list.stream()
.map(i -> {
if (i%2==0) {
return "e"+i;
} else {
return "u"+i;
}
})
.collect(Collectors.joining(","));
}

Related

Convert an for each loop with var inside to a Java Stream?

I have a function:
String fun(List<Function<String, String>> pro, String x){
for(var p: pro){
x = p.apply(x);
}
return x;
}
How can I convert this function to functional style instead of imperative style?
Assuming what you want is to apply each function to your string, passing along the result of each function to the next, you can do this with reduce.
String fun(List<Function<String, String>> functions, String x) {
return functions.stream()
.reduce(s -> s, Function::andThen)
.apply(x);
}
Using reduce with andThen creates a combined function that chains your list of functions together. We then apply the combined function to x.
Alternatively, #Naman in the comments suggests the formulation:
functions.stream()
.reduce(Function::andThen)
.orElse(Function.identity())
.apply(x)
which I believe performs one fewer andThen operation (when the list of functions is nonempty), but is functionally the same as the first version.
(Function.identity() is an another way to write s -> s.)
I believe you are already aware about those compilation errors. You can't just define List<Function<>> without having a common understanding about those list of functions. Maybe you can get some inspiration from below code snippet.
String fun(List<Function<String, String>> listOfFunctions, String commonInputStr){
for (Function<String, String> function : listOfFunctions) {
String tempValStr = function.apply(commonInputStr);
if (tempValStr != null){
return tempValStr;
}
}
return null;
}
Or if you want to find the first result value like below:
Optional<String> fun(List<Function<String, String>> listOfFunctions, String commonInputStr){
return listOfFunctions.stream()
.map(stringStringFunction -> stringStringFunction.apply(commonInputStr))
.findFirst();
}

Save Value to List from Stream

Currently im learning about stream and want to implement a method which accepts a string. The String starts with a certain word and ends with the same. The given example is "breadtunabread". The method return the word in between the bread.
public String getTopping(String s){
Stream<String> stream = Stream.of(s);
stream.filter(t -> t.startsWith("bread") && t.endsWith("bread")).
forEach(t -> Stream.of(t.split("bread")[1]).collect(Collectors.toList()));
}
I'd like to either save it to a List or change it directly so it returns a String.
Is it possible to get the first value from the stream and not use collect?
I somehow made it work using forEach and adding the value to an ArrayList and returning it but i'd like to know whether there is a way to do it directly using the stream.
Thanks in advance.
And to return just a String:
public String getTopping(String s, String toReplace) {
Stream<String> stream = Stream.of(s);
return stream.filter(t -> t.startsWith(toReplace) && t.endsWith(toReplace))
.findFirst()
.map(t -> t.replaceAll(toReplace, ""))
.orElseThrow(RuntimeException::new);
//.orElseThrow(() -> new NoBreadException("s"));
}
Stream<String> stream = Stream.of("breadtunabread");
List<String> stringList =
stream
.filter(t -> t.startsWith("bread") && t.endsWith("bread"))
.map(t -> (t.split("bread")[1]))
.collect(Collectors.toList());
Is this what you are looking for?
What others mentioned are correct that this is completely unnecessary. I posted this as you have mentioned that yuo are learning streams.
Just like #Naman pointed out, you don't need a Stream for this. String#replaceAll will quite literally replace all instances of the String (bread) with empty String values and in the end you get you're topping. Added the base parameter in case you're a monster like me and eat cheese between pieces of ham.
public static String getTopping(String value, String base) {
return value.replaceAll(base, "");
}
String topping = getTopping("breadtunabread", "bread")
Assuming you have a List of items you want to get the toppings of.
List<String> sandwhiches = Arrays.asList(
"breadtunabread",
"breadchickenbread",
"breadcheesebread",
"breadturkeybread",
"breadlambbread"
);
List<String> toppings = sandwhiches.stream()
.map(sandwhich -> getTopping(sandwhich, "bread"))
.collect(Collectors.toList());
Result
[tuna, chicken, cheese, turkey, lamb]

Java 8 find first instance of value in multidimensional array of objects

Trying to understand how to use some java 8 features and was playing around with multidimensional array of objects, if I wanted to find the first instance of a value in a multidimensional array of objects.
Objects[][] someArray= .....
Arrays.stream(someArray)
.map(someArrayFirst -> Arrays.stream(someArrayFirst))
.map(unsure what to do here)
.filter(a -> a.equals("some value"))
.findFirst();
edit, thanks for the input. Just to help others out here is what I have now.
Arrays.stream(someArray)
.flatMap(someArrayFirst -> Arrays.stream(someArrayFirst))
.filter(MyCustomClass.class::isInstance)
.map(MyCustomClass.class::cast)
.filter(v -> v.value().equalsIgnoreCase("SomeString"))
.findFirst();
You are on the right track. You need to turn the two dimensions into a single stream and then take the first element that satisfies your condition:
String[][] array;
Optional<String> result = Arrays.stream(array)
.flatMap(Arrays::stream)
.filter("some value"::equals).findFirst();
The first stream produces a Stream<String[]>. The flat map turns each of the array elements into a Stream<String>. Then it's just filtering for the value you want and getting the first element that satisfies your condition.
static String[][] arr = new String[][]{{"x","y"},{"z","v"},{"b","z"}};
static String searchStr = "x";
static String searchObj = null;
public static void main(String... args) {
Arrays.stream(arr)
.forEach((subarr)->{
Optional<String> opt = Arrays.stream(subarr)
.filter((obj)->obj.equals(searchStr))
.findFirst();
if (opt.isPresent())
searchObj = opt.get();
});
System.out.println(searchObj);
}
or
static public String mapFlatMethod(String[][] arr, String searchStr) {
return Arrays.stream(arr).flatMap(row -> Stream.of(row))
.filter((obj)->obj.equals(searchStr))
.findFirst().get();
}

How to convert a String of Integers to an ArrayList in Java?

I try to parse a textfile which has lines which look like the following:
#KEY,_,0,1,2,_,4,5,6, ...
The #KEY is just an identifier in the beginning while the following numbers are my data which I want to store in an ArrayList<Integer>.
I have a metadata class which contains the arraylist in which I want to insert there integers:
class MetaD {
public List<Integer> key1, key2, key3 = new ArrayList<Integer>();
}
I parse the textfile line by line; when the line starts with #KEY, I want to add the elements to the key1 list. If there is an _, it should be replaced with an empty value:
if(line.startsWith("#KEY")){
metaObject.key1 = Arrays.asList(line.replace("#KEY,", "").replace("_", "").trim().split("\\s*,\\s*"));
}
I found out that this does not work with ArrayList<Integer>. key1 has to be of the type ArrayList<String> or ArrayList<Object> to make it work.
Is there a way to convert Integers in the same way?
If not, my idea would be the following:
Convert everything to an ArrayList<String>
Iterate every item of this new ArrayList and convert it with Integer.parseInt() into an Integer.
Adding this new Integer to my ArrayList<Integer>
Would there be a more efficient or better way to archive my needs?
Edit:
Since Tunaki wrote in the comments, that my idea will probably be the only possible way I tried to do the following:
if(line.startsWith("#KEY")){
List<String> channelTemp = Arrays.asList(line.replace("#KEY,", "").replace("_", "1").split("\\s*,\\s*"));
channelTemp.forEach(item -> metaObject.channel.add(Integer.parseInt(item)));
System.out.println("done");
}
Unfortunately, this throws a NullPointerException in the third line here and I don't have a clue why. I replaced _ with 1 for testing purposes to avoid a NumberFormatException. When I print out every object in the lambda function instead of adding them to my ArrayList<Integer>, I can see that all items have an Integer value. So why do I get an exception here?
Since you're almost there I'll give you a hand.
String line = "#KEY,_,0,1,2 , _,4,5,6,";
List<Integer> collect = Arrays.stream(line.replaceAll("#KEY|_", "").split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.map(Integer::valueOf).collect(Collectors.toList());
System.out.println(collect);
EDIT
To obtain the null you can alter the mapping process like:
List<Integer> collect = Arrays.stream(line.split(","))
.skip(line.startsWith("#KEY") ? 1 : 0)
.map(String::trim)
.filter(s -> !s.isEmpty())
.map(s -> "_".equals(s) ? null : Integer.valueOf(s)).collect(Collectors.toList());
You're trying to put in list of Integer a String:
metaObject.key1 = Arrays.asList(line.replace("#KEY,", "").replace("_", "").trim().split("\\s*,\\s*"));
Here line.replace(...) and trim() return a String, and split(...) returns a String[].
Therefore Arrays.asList(...) returns a List<String> here, that's not compatible with your definition of key1 (List<Integer>).
Yes, you can convert it to List<Integer> by call Integer.valueOf(...) or Integer.parseInt(...).
But I would recommend to
Use a new instance of List instead of Arrays.asList(...) because the latest one will produce an unmodifiable collection. Sometines it's not what you want :)
Use something less specific than your own text format. What about JSON? There are a lot of libraries to simplify parsing/storing of the data.
Firstly, you should split your string with ",", then you try if your each String is an integer or not with an isIntegerMethod. If it is an integer, you can add it into the list.
public static void main(String[] args) throws IOException {
String str = "#KEY,_,0,1,2,_,4,5,9";
String [] strArr = str.split(",");
List<Integer> intList = new ArrayList<Integer>();
for (String string : strArr) {
if (isInteger(string, 10)) {
intList.add(Integer.valueOf(string));
} else {
System.out.println(string + " is not an integer");
}
}
System.out.println(intList.toString());
}
public static boolean isInteger(String s, int radix) {
if(s.isEmpty()) return false;
for(int i = 0; i < s.length(); i++) {
if(i == 0 && s.charAt(i) == '-') {
if(s.length() == 1) return false;
else continue;
}
if(Character.digit(s.charAt(i),radix) < 0) return false;
}
return true;
}

Java Streams: Is there a cleaner way of doing this?

I am using streams to concatenate a series of strings and add commas between them, but there must be no comma at the beginning or the end of the result string.
import java.util.Arrays;
import java.util.List;
public class QuestionNine {
public static void main(String[] args) {
new QuestionNine().launch();
}
public void launch(){
List<String> words = Arrays.asList("Hello", "Bonjour", "engine", "Hurray", "What",
"Dog", "boat", "Egg", "Queen", "Soq", "Eet");
String result = (words.stream().map(str -> str + ",").reduce("", (a,b) -> a + b));
result = result.substring(0, result.length() -1); //removes last comma
System.out.println(result);
}
}
Instead of using the String.substring() method at the end to get rid of the last comma, is there a way i could have deleted the last comma within the stream pipeline?
The usual idiom is to use the joining Collector with Streams.
String res = words.stream().collect(Collectors.joining(","));
Although you can use String.join in your case since you are directly dealing with an Iterable.
String res = String.join(",", words);
The problem with your approach is that the mapping function you apply impose that there will be a comma at the end of each word. You could get rid of this mapping; and apply the reduce function such that you get the desired output:
.stream().reduce("", (a,b) -> a.isEmpty() ? b : a+","+b);
but I don't recommend this.
Yes, you can use Collectors.joining() here:
String joined = words.stream().collect(Collectors.joining(", "));
Or, also as noted from comments, you can use newly added String.join(CharSequence, Iterable) method.
String joined = String.join(", ", words);

Categories

Resources