I'm new to functional methods like reduce and fold in java 8 and Kotlin. I want to reduce -
List<List<List<Double>>> boundingPolygon = [[[-125.48845080566404,47.94508483691371],[-124.96110705566404,42.309040799653665],[-117.13884143066404,45.04173793121063],[-118.36931018066404,48.93624688577435],[-125.48845080566404,47.94508483691371]]];
to single string that represents one String concatenated by coordinates -
"-118.359053 33.931562,-118.372443 33.946939,-118.369053 33.951562,-118.337612 33.944342,-118.342012 33.944042,-118.359053 33.931562"
Trying to do -
val polygonCoordinates = boundingPolygon.first().reduce { acc, element ->
acc + "${element[0]} ${element[1]}"
}
This is not working.
The acc in your reduce operation is of type List<Double>, not String. Look at the reduce function signature and you should understand why. Here's what I propose to do what you want:
coords.first().joinToString(", ") { (x, y) -> "$x $y" }
I used destructuring to extract the first and second values from the coordinate lists. So this will only work with 2D coordinates.
Instead of Reduce, you can use flatMap. It will help you.
List<List<List<Double>>> boundingPolygon = List.of(List.of(List.of(-124.96110705566404, 42.309040799653665)
, List.of(-117.13884143066404, 45.04173793121063)
, List.of(118.36931018066404, 48.93624688577435)
));
var l = boundingPolygon.stream().flatMap(lists -> lists.stream().flatMap(doubles -> doubles.stream())).collect(Collectors.toList());
System.out.println(l);
It will print an output as below.
[-124.96110705566404, 42.309040799653665, -117.13884143066404, 45.04173793121063, 118.36931018066404, 48.93624688577435]
Try above code, this will help you.
Instead of reducing it, you should just add those into a StringBuilder, which is efficient when you are doing multiple operations (like concatenating a ton of Strings):
val boundingPolygon = listOf(
listOf(
listOf(-125.48845080566404, 47.94508483691371),
listOf(-124.96110705566404, 42.309040799653665),
listOf(-117.13884143066404, 45.04173793121063),
listOf(-118.36931018066404, 48.93624688577435),
listOf(-125.48845080566404, 47.94508483691371)
)
)
val sb = StringBuilder()
for (nestedList in boundingPolygon) {
for (innerNestedList in nestedList) {
sb.append(innerNestedList.joinToString(" "))
sb.append(',')
}
}
if (sb.isNotEmpty()) sb.deleteCharAt(sb.lastIndex)
println(sb)
// Output: -125.48845080566404 47.94508483691371,-124.96110705566404 42.309040799653665,-117.13884143066404 45.04173793121063,-118.36931018066404 48.93624688577435,-125.48845080566404 47.94508483691371
// val stringRepresentation = sb.toString() // for further use as String data-type
Related
I have a stream of data as shown below and I wish to collect the data based on a condition.
Stream of data:
452857;0;L100;csO;20220411;20220411;EUR;000101435;+; ;F;1;EUR;000100000;+;
452857;0;L120;csO;20220411;20220411;EUR;000101435;+; ;F;1;EUR;000100000;+;
452857;0;L121;csO;20220411;20220411;EUR;000101435;+; ;F;1;EUR;000100000;+;
452857;0;L126;csO;20220411;20220411;EUR;000101435;+; ;F;1;EUR;000100000;+;
452857;0;L100;csO;20220411;20220411;EUR;000101435;+; ;F;1;EUR;000100000;+;
452857;0;L122;csO;20220411;20220411;EUR;000101435;+; ;F;1;EUR;000100000;+;
I wish to collect the data based on the index = 2 (L100,L121 ...) and store it in different lists of L120,L121,L122 etc using Java 8 streams. Any suggestions?
Note: splittedLine array below is my stream of data.
For instance: I have tried the following but I think there's a shorter way:
List<String> L100_ENTITY_NAMES = Arrays.asList("L100", "L120", "L121", "L122", "L126");
List<List<String>> list= L100_ENTITY_NAMES.stream()
.map(entity -> Arrays.stream(splittedLine)
.filter(line -> {
String[] values = line.split(String.valueOf(DELIMITER));
if(values.length > 0){
return entity.equals(values[2]);
}
else{
return false;
}
}).collect(Collectors.toList())).collect(Collectors.toList());
I'd rather change the order and also collect the data into a Map<String, List<String>> where the key would be the entity name.
Assuming splittedLine is the array of lines, I'd probably do something like this:
Set<String> L100_ENTITY_NAMES = Set.of("L100", ...);
String delimiter = String.valueOf(DELIMITER);
Map<String, List<String>> result =
Arrays.stream(splittedLine)
.map(line -> {
String[] values = line.split(delimiter );
if( values.length < 3) {
return null;
}
return new AbstractMap.SimpleEntry<>(values[2], line);
})
.filter(Objects::nonNull)
.filter(tempLine -> L100_ENTITY_NAMES.contains(tempLine.getEntityName()))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList());
Note that this isn't necessarily shorter but has a couple of other advantages:
It's not O(n*m) but rather O(n * log(m)), so it should be faster for non-trivial stream sizes
You get an entity name for each list rather than having to rely on the indices in both lists
It's easier to understand because you use distinct steps:
split and map the line
filter null values, i.e. lines that aren't valid in the first place
filter lines that don't have any of the L100 entity names
collect the filtered lines by entity name so you can easily access the sub lists
I would convert the semicolon-delimited lines to objects as soon as possible, instead of keeping them around as a serialized bunch of data.
First, I would create a model modelling our data:
public record LBasedEntity(long id, int zero, String lcode, …) { }
Then, create a method to parse the line. This can be as well an external parsing library, for this looks like CSV with semicolon as delimiter.
private static LBasedEntity parse(String line) {
String[] parts = line.split(";");
if (parts.length < 3) {
return null;
}
long id = Long.parseLong(parts[0]);
int zero = Integer.parseInt(parts[1]);
String lcode = parts[2];
…
return new LBasedEntity(id, zero, lcode, …);
}
Then the mapping is trivial:
Map<String, List<LBasedEntity>> result = Arrays.stream(lines)
.map(line -> parse(line))
.filter(Objects::nonNull)
.filter(lBasedEntity -> L100_ENTITY_NAMES.contains(lBasedEntity.lcode()))
.collect(Collectors.groupingBy(LBasedEntity::lcode));
map(line -> parse(line)) parses the line into an LBasedEntity object (or whatever you call it);
filter(Objects::nonNull) filters out all null values produced by the parse method;
The next filter selects all entities of which the lcode property is contained in the L100_ENTITY_NAMES list (I would turn this into a Set, to speed things up);
Then a Map is with key-value pairs of L100_ENTITY_NAME → List<LBasedEntity>.
You're effectively asking for what languages like Scala provide on collections: groupBy. In Scala you could write:
splitLines.groupBy(_(2)) // Map[String, List[String]]
Of course, you want this in Java, and in my opinion, not using streams here makes sense due to Java's lack of a fold or groupBy function.
HashMap<String, ArrayList<String>> map = new HashMap<>();
for (String[] line : splitLines) {
if (line.length < 2) continue;
ArrayList<String> xs = map.getOrDefault(line[2], new ArrayList<>());
xs.addAll(Arrays.asList(line));
map.put(line[2], xs);
}
As you can see, it's very easy to understand, and actually shorter than the stream based solution.
I'm leveraging two key methods on a HashMap.
The first is getOrDefault; basically if the value associate with our key doesn't exist, we can provide a default. In our case, an empty ArrayList.
The second is put, which actually acts like a putOrReplace because it lets us override the previous value associated with the key.
I hope that was helpful. :)
you're asking for a shorter way to achieve the same, actually your code is good. I guess the only part that makes it look lengthy is the if/else check in the stream.
if (values.length > 0) {
return entity.equals(values[2]);
} else {
return false;
}
I would suggest introduce two tiny private methods to improve the readability, like this:
List<List<String>> list = L100_ENTITY_NAMES.stream()
.map(entity -> getLinesByEntity(splittedLine, entity)).collect(Collectors.toList());
private List<String> getLinesByEntity(String[] splittedLine, String entity) {
return Arrays.stream(splittedLine).filter(line -> isLineMatched(entity, line)).collect(Collectors.toList());
}
private boolean isLineMatched(String entity, String line) {
String[] values = line.split(DELIMITER);
return values.length > 0 && entity.equals(values[2]);
}
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();
}
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]
this is my first post on Stack Overflow, so please, be forgiving! :)
I have a list with 403 Polish registration plates symbols and counties. It looks like this:
BIA powiat białostocki
BBI powiat bielski
BGR powiat grajewski
CT Toruń
etc.
I made a code which let me to turn the first space into "=".
import java.io.*;
public class Test {
public static void main(String args[]) {
String Str = new String("BAU powiat augustowski");
System.out.println(Str.replaceFirst(" ", "="));
}
}
How can I make a loop (for? do while?) to change all 403 records? I would appreciate any help. Thank you in advance!
If your list is a List<String>, you can do this :
for (for int i = 0, i < yourList.size(), i++) {
yourList.set(i, yourList.get(i).replaceFirst(" ", "="));
}
Other ways to loop are available here : https://crunchify.com/how-to-iterate-through-java-list-4-way-to-iterate-through-loop/
Best
You COULD also use Stream API. Eg if you want to filter all invalid strings
List<String> registrations = new ArrayList<>(5);
registrations.add("BIA powiat białostocki");
registrations.add("BBI powiat bielski");
registrations.add("BGR powiat grajewski");
registrations.add("BGGHND");
registrations.add("CT Toruń etc.");
registrations = registrations.stream()
.filter(registration -> registration.split(" ").length>1)
.map(registration -> registration.replaceFirst(" ","="))
.collect(Collectors.toList());
Output:
BIA=powiat białostocki
BBI=powiat bielski
BGR=powiat grajewski
CT=Toruń etc.
As you mentioned how can make a loop then I would suggest to learn java loop syntax at first. Following tutorial can be helpful
https://books.trinket.io/thinkjava2/chapter6.html
https://www.codingame.com/playgrounds/6162/6-ways-to-iterate-or-loop-a-map-in-java
Regarding the solution, you can loop your lists using for/while loop or using Stream API. Here is your solution using stream API:
List<String> lists = new ArrayList<>();
lists.add("BAU powiat augustowski");
lists.add("BBI powiat bielski");
lists = lists.stream()
.map(s -> s.replaceFirst("\\s", "="))
.collect(toList());
Either if you are using an ArrayList or a HashSet you can use two ways:
Fyi: assuming your lists name is registrationList and it holds Objects names Registration
Either a for loop:
for(Registration registration : registrationList){
registration.replaceFirst(" ", "=");
}
Or you can use a Stream:
registrationList.stream.forEach(registration-> registration.replaceFirst(" ", "="));
If you have all lines in txt file and you want to modify that by replacing first space to = you could use stream API like:
List<String> collect = Files.lines(Paths.get(PATH_TO_FILE)).stream()
.map(s -> s.replaceFirst(" ", "="))
.collect(toList());
Files.write(PATH_TO_FILE, collect, StandardOpenOption.CREATE);
See more StandardOpenOption
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.