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();
}
Related
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]
I've got a List<String> which represents the ID's (can be duplicate), of items from another list, which is a List<Cheat>, where each Cheat has a String ID and a List<Integer> RNG. Both have accessor methods in Cheat.
I need to convert this list of ID's, into a list of RNG's for each Cheat that I have been supplied with the ID for.
For example, I could have 3 Cheats:
1:{ID:1, RNG:{1,2,3}}
2:{ID:2, RNG{1,2}}
3:{ID:3, RNG:{1}}
And a List of ID's of:
{3,1,1,2}.
I would need to end up with a final list of {1,1,2,3,1,2,3,1,2}, which is the RNG's of Cheat 3, then the RNG's of cheat 1, then the RNG's of cheat 1 again, then finally the RNG's of cheat 2.
If anyone could help me out it would be appreciated. Thank you.
I've tried and failed with:
ImmutableList<Integer> sequenceRngs = cheatIds.stream()
.map(s -> cheats.stream()
.filter(cheat -> cheat.getId().equals(s))
.findFirst()
.map(cheat -> cheat.getRng()))
.flatMap(cheat -> cheat.getRng())
.collect(ListUtils.toImmutableList());
One possible solution:
import java.util.List;
import java.util.stream.Collectors;
class Scratch {
static class Cheat {
int id;
List<Integer> rng;
public Cheat(int id, List<Integer> rng) {
this.id = id;
this.rng = rng;
}
}
public static void main(String[] args) {
List<Cheat> allCheats = List.of(
new Cheat(1, List.of(1,2,3)),
new Cheat(2, List.of(1,2)),
new Cheat(3, List.of(1))
);
List<Integer> result = List.of(3, 1, 1, 2).stream()
.flatMap(id -> allCheats.stream()
.filter(cheat -> cheat.id == id)
.findFirst().orElseThrow().rng.stream())
.collect(Collectors.toList());
System.out.println(result);
}
}
The key is to use flatMap to get the result in a single - not nested - Collection in the end.
The lambda that you pass to flatMap should return a Stream, not a List. And you should handle the case where there's no such element in the stream - even if you are sure there is. Something like this should do:
final ImmutableList<String> sequenceRngs = cheatIds.stream().flatMap(id ->
cheats.stream().filter(cheat -> id.equals(cheat.getId()))
.findAny().orElseThrow(IllegalStateException::new)
.getRng().stream())
.collect(ListUtils.toImmutableList());
Also, I would propose to convert the list of cheats to a map - that would simplify the code and reduce the complexity of searching from O(n) to O(1).
You can attain that with the following steps:
Create a map of cheatId to RNG ids associated:
Map<Integer, List<Integer>> map = cheats.stream()
.collect(Collectors.toMap(Cheat::getId,
cheat -> cheat.getRng().stream().map(RNG::getId).collect(Collectors.toList())));
Iterate over the cheatIds provided as input and get the corresponding RNG ids from the map to collect as output:
List<Integer> output = cheatIds.stream()
.flatMap(ch -> map.get(ch).stream())
.collect(Collectors.toList());
In my program, I am trying to print sorted int array using stream. But I am getting false output while using normal stream. And correct details are getting printed while using int stream.
Please refer below core snippet for more details.
package com.test.sort.bubblesort;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class BubbleSortWithRecursion {
public static void bubbleSort(int[] arr, int n) {
if (n < 2) {
return;
}
int prevValue;
int nextValue;
for (int index = 0; index < n-1; index++) {
prevValue = arr[index];
nextValue = arr[index+1];
if (prevValue > nextValue) {
arr[index] = nextValue;
arr[index+1] = prevValue;
}
}
bubbleSort(arr, n-1);
}
public static void main(String[] args) {
int arr[] = new int[] {10,1,56,8,78,0,12};
bubbleSort(arr, arr.length);
**//False Output** : [I#776ec8df
String output = Arrays.asList(arr)
.stream()
.map(x -> String.valueOf(x))
.collect(Collectors.joining(","));
System.out.println(output);
//Correct Output : 0,1,8,10,12,56,78
String output2 = IntStream
.of(arr)
.boxed()
.map(x -> Integer.toString(x))
.collect(Collectors.joining(","));
System.out.println(output2);
}
}
And I am getting following output on console :
[I#776ec8df
0,1,8,10,12,56,78
The fist line of output was generated using normal java stream which is not correct.
Why am I getting false content using normal JAVA stream ? Am I missing something here ?
You can solve your issue like so :
String output = Arrays.stream(arr)
.boxed()
.map(String::valueOf)
.collect(Collectors.joining(",")); // 0,1,8,10,12,56,78
Explain what happen :
when you use Arrays.asList() which look :
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
it took varargs of type T, in your case you use it for int[] Object, so Arrays.asList() will return List of int[] and not a stream of ints, so instead you have to use Arrays.stream which look like this :
public static IntStream stream(int[] array) {
return stream(array, 0, array.length);
}
to get the correct data.
Arrays.asList(arr) returns a List<int[]> whose only element is arr. Therefore streaming that List and then mapping that single element to String.valueOf(x) and collecting with Collectors.joining(",") will result in a String whose value is that single array's toString(), which is the output you see.
String output = Arrays.asList(arr) // List<int[]>
.stream() // Stream<int[]>
.map(x -> String.valueOf(x)) // Stream<String> having a single element - "[I#776ec8df"
.collect(Collectors.joining(",")); // "[I#776ec8df"
When you create an IntStream from the int array, you get a stream of the individual elements (the int values), so you can box them, convert then to Strings and join them to get the desired output.
You can make your first snippet work if you change:
int arr[] = new int[] {10,1,56,8,78,0,12};
to:
Integer arr[] = new Integer[] {10,1,56,8,78,0,12};
since this way Arrays.asList(arr) will produce a List<Integer> containing all the elements of the input array.
You can do it even a bit shorter:
String output = Arrays.stream(arr)
.mapToObj(String::valueOf)
.collect(Collectors.joining(","));
The result is 0,1,8,10,12,56,78
First you create an IntStream, then you get a Stream<String> which is collected to the final String.
I have three comma-separated lists (list of bus, car, cycle) and I am trying to write them into Java object properties using Java 8 streams.
Please find below what I have tried :
public class Traffic {
public int car;
public int bus;
public int cycle;
public Traffic(int car, int bus,int cycle){
this.car = car;
this.bus = bus;
this.cycle = cycle;
}
}
public class Test {
public static void main(String[] args) {
String bus = "5,9,15,86";
String car = "6,12,18,51";
String cycle = "81,200,576,894";
String[] busArray = bus.split(",");
String[] carArray = car.split(",");
String[] cycleArray = cycle.split(",");
List<Traffic> trafficList =
Arrays.stream(values)
.mapToInt(Integer::parseInt)
.mapToObj((int i,j) -> new Traffic(i,j))
.collect(Collectors.toList());
}
}
I was struggling with getting all streams up and injected into object properties. (I want to create 4 objects in this case populating all 3 properties.)
Basically, I am looking for something like below:
List<Traffic> trafficList =
Arrays.stream(carArray,busArray,cycleArray)
.mapToInt(Integer::parseInt)
.mapToObj((int i,j,k) -> new Traffic(i,j,k))
.collect(Collectors.toList());
If you want to create 4 objects of Traffic then you can use the following :
List<Traffic> collect = IntStream.range(0, busArray.length)
.mapToObj(i -> new Traffic(Integer.parseInt(busArray[i]),
Integer.parseInt(carArray[i]),
Integer.parseInt(cycleArray[i])))
.collect(Collectors.toList());
You just have to split your string and then map each value to your object.
Here I assume the value can be passed through the constructor of your Traffic object. If not, you can create it and set its value in 2 separate lines. The mapToInt is necessary if the value is expected to be an integer.
String original = "5,9,15,86";
String[] values = original.split(",");
List<Traffic> trafficList =
Arrays.stream(values)
.mapToInt(Integer::parseInt)
.map(Traffic::new)
.collect(Collectors.toList());
Define a constructor in the class Traffic that takes an integer as argument and assigns it to value attribute in the class.
static class Traffic {
private int value;
public Traffic(int value) {
this.value = value;
}
}
Now assuming the comma delimited string is in a string commandList, something like below.
String commaList = "1,3,5,6,7,8,9,100";
Following stream instruction will return a list of Traffic objects with the value assigned.
List<Traffic> listOfIntegers =
Arrays.asList(commaList.split(","))
.stream()
.map(e -> new Traffic(Integer.valueOf(e)))
.collect(Collectors.toList());
If you really want an array, you can try the following
Arrays.stream("5,9,15,86".split(","))
.map(Traffic::new)
.toArray(Traffic[]::new);
If a List<Traffic> is also okay for you i recommend this one
Arrays.stream("5,9,15,86".split(","))
.map(Traffic::new)
.collect(Collectors.toList());
And lastly if you only have a constructor for Integer for example, you can map the stram to int like
Arrays.stream("5,9,15,86".split(","))
.mapToInt(Integer::valueOf)
.mapToObj(Traffic::new)
.collect(Collectors.toList());
EDIT
I answered this question before the question was edited, that's why it is only a partial answer
EDIT2
Okay i got it, i used map instead of mapToObj what a huge mistake... But i found it out thanks to #JavaMan's helpful answers (notice that if you are using IntelliJ it offers you to replace map with mapToObj)
I got an array of elements like :
ArrayList<String> t = new ArrayList();
t.add("/folder1/sub-folder1");
t.add("/folder2/sub-folder2");
t.add("/folder1/sub-folder1/data");
I need to get output as /folder1/sub-folder1 which is mostly repeated path.
In python this can be achieved using the below function:
def getRepeatedPath(self, L):
""" Returns the highest repeated path/string in a provided list """
try:
pkgname = max(g(sorted(L)), key=lambda(x, v): (len(list(v)), -L.index(x)))[0]
return pkgname.replace("/", ".")
except:
return "UNKNOWN"
I am trying to work on equivalent lambda function in Java. I got struck and need some help in the lambda implementation.
public String mostRepeatedSubString(ArrayList<String> pathArray) {
Collections.sort(pathArray);
String mostRepeatedString = null;
Map<String,Integer> x = pathArray.stream.map(s->s.split("/")).collect(Collectors.toMap());
return mostRepeatedString;
}
Lots of tweaking, but I finally got it!
public static void main(String[] args) {
ArrayList<String> t = new ArrayList<String>();
t.add("folder1/sub-folder1");
t.add("folder2/sub-folder2");
t.add("folder1/sub-folder1/data");
System.out.println(mostRepeatedSubString(t));
}
public static String mostRepeatedSubString(List<String> pathArray) {
return pathArray
.stream()
// Split to lists of strings
.map(s -> Arrays.asList(s.split("/")))
// Group by first folder
.collect(Collectors.groupingBy(lst -> lst.get(0)))
// Find the key with the largest list value
.entrySet()
.stream()
.max((e1, e2) -> e1.getValue().size() - e2.getValue().size())
// Extract that largest list
.map(Entry::getValue)
.orElse(Arrays.asList())
// Intersect the lists in that list to find maximal matching
.stream()
.reduce(YourClassName::commonPrefix)
// Change back to a string
.map(lst -> String.join("/", lst))
.orElse("");
}
private static List<String> commonPrefix(List<String> lst1, List<String> lst2) {
int maxIndex = 0;
while(maxIndex < Math.min(lst1.size(), lst2.size())&& lst1.get(maxIndex).equals(lst2.get(maxIndex))) {
maxIndex++;
}
return lst1.subList(0, maxIndex);
}
Note that I had to remove the initial / from the paths, otherwise that character would have been used in the split, resulting in the first string in every path list being the empty string, which would always be the most common prefix. Shouldn't be too hard to do this in pre-processing though.