I am trying to convert the below nested loop in to streams Java 8.
Each element in newself2 is a list of string - ["1 2","3 4"] needs to change to ["1","2","3","4"].
for (List<String> list : newself2) {
// cartesian = [["1 2","3 4"],["4 5","6 8"]...] list = ["1 2","3 4"]...
List<String> clearner = new ArrayList<String>();
for (String string : list) { //string = "1 3 4 5"
for (String stringElement : string.split(" ")) {
clearner.add(stringElement);
}
}
newself.add(clearner);
//[["1","2","3","4"],["4","5","6","8"]...]
}
What I have tried till now -
newself2.streams().forEach(list -> list.foreach(y -> y.split(" ")))
Now I am now sure how to add the split array in the inner for loop to a new list for x?
Any help is greatly appreciated.
Here's how I'd do it:
List<List<String>> result = newself2.stream()
.map(list -> list.stream()
.flatMap(string -> Arrays.stream(string.split(" ")))
.collect(Collectors.toList()))
.collect(Collectors.toList());
This is other solution.
Function<List<String>,List<String>> function = list->Arrays.asList(list.stream()
.reduce("",(s, s2) -> s.concat(s2.replace(" ",",")+",")).split(","));
and use this function
List<List<String>> finalResult = lists
.stream()
.map(function::apply)
.collect(Collectors.toList());
with for loop is similar to this:
List<List<String>> finalResult = new ArrayList<>();
for (List<String> list : lists) {
String acc = "";
for (String s : list) {
acc = acc.concat(s.replace(" ", ",") + ",");
}
finalResult.add(Arrays.asList(acc.split(",")));
}
Related
I need to convert this code using streams but I don't know how to do it.
List<String> lines = Files2.linesFromFile(ruta);
List<List<String>> res = new ArrayList<>();
for (String line : lines) {
List<String> aux = new ArrayList<>();
for (String element : line.split(",")) {
aux.add(element);
}
res.add(aux);
}
If I show the content of res I got:
[[ejercicios, practica, propuesta], [cadena, recomendar, definir], [elemento, implementar, sol], [ala, map, public, static]]
The content of lines is:
[ejercicios,practica,propuesta, cadena,recomendar,definir, elemento,implementar,sol, ala,map,public,static]
And I need to get the same output using stream, can somebody help me?
Thanks in advance.
Using #Hulk's suggestion for Pattern.splitAsStream, you can replace your whole for...loop with:
res = lines.stream().map(l -> Pattern.compile(",").splitAsStream(l).collect(Collectors.toList()))
.collect(Collectors.toList());
res value is:
[[ejercicios, practica, propuesta], [cadena, recomendar, definir], [elemento, implementar, sol]]
If you want to "flatten" you List of Lists of Strings (List<List<String>> res) and make it just one List of Strings (List<String>)
you can use flatMap as:
List<String res = lines.stream().flatMap(l -> Pattern.compile(",").splitAsStream(l))
.collect(Collectors.toList());
or even better as #user16320675
suggested:
res = lines.stream().flatMap(Pattern.compile(",")::splitAsStream)
.collect(Collectors.toList());
this outputs:
[ejercicios, practica, propuesta, cadena, recomendar, definir, elemento, implementar, sol]
Use Stream::map to transform each line and Stream::collect to create a new list from the stream.
Replacement for your code would be:
List<List<String>> res = lines.stream()
.map(line -> {
List<String> aux = new ArrayList<>();
for (String element : line.split(",")) {
aux.add(element);
}
return aux;
})
.collect(Collectors.toList());
But we can simplify it by using Arrays::asList to create List<String> from String[] without writing the for loop.
List<List<String>> res = lines.stream()
.map(line -> Arrays.asList(line.split(",")))
.collect(Collectors.toList());
I have a List<String> that may or not contain duplicated values:
In the case of duplicated "ABC" value (only ABC for this matter)
List myList = {"ABC", "EFG", "IJK", "ABC", "ABC"},
I want to split the list in two lists to finally get
List duplicatedValues = {"ABC"};
and
List nonDuplicatedValues = {"EFG", "IJK"};
And also if the list doesn't have more than one "ABC" it will return the same list
What I did so far :
void generateList(List<String> duplicatedValues, List<String> nonDuplicatedValues){
List<String> myList=List.of("ABC","EFG","IJK","ABC","ABC");
Optional<String> duplicatedValue = myList.stream().filter(isDuplicated -> Collections.frequency(myList, "ABC") > 1).findFirst();
if (duplicatedValue.isPresent())
{
duplicatedValues.addAll(List.of(duplicatedValue.get()));
nonDuplicatedValues.addAll(myList.stream().filter(string->string.equals("ABC")).collect(Collectors.toList()));
}
else
{
nonDuplicatedValues.addAll(myList);
}
}
Is there a more efficient way to do that using only a stream of myList ?
You can do something like this:
myList.stream().forEach((x) -> ((Collections.frequency(myList, x) > 1) ? duplicatedValues : nonDuplicatedValues).add(x));
(The duplicatedValues should be a Set to prevent duplications)
Also it can be done by collecting to lists of duplicated and non-duplicated values:
Map<Boolean, List<String>> result = input.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(s -> s, Collectors.counting()),
m -> m.entrySet().stream()
.collect(Collectors.groupingBy(e -> e.getValue() > 1,
Collectors.mapping(e -> e.getKey(), Collectors.toList()))
)
));
List<String> duplicates = result.get(true);
List<String> nonDuplicates = result.get(false);
It is possible to use a stream to create from your list a Map storing strings and their frequencies in your list; after you can iterate over the map to put elements in lists duplicatedValues and nonDuplicatedValues like below:
List<String> duplicatedValues = new ArrayList<String>();
List<String> nonDuplicatedValues = new ArrayList<String>();
List<String> myList=List.of("ABC","EFG","IJK","ABC","ABC");
Map<String, Long> map = myList.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
map.forEach((k, v) -> { if (v > 1) duplicatedValues.add(k); else nonDuplicatedValues.add(k); });
Here is one way to do it. It basically does a frequency count and divdes accordingly.
List<String> myList = new ArrayList<>(
List.of("ABC", "EFG", "IJK", "ABC", "ABC", "RJL"));
Map<String,Long> freq = new HashMap<>();
for (String str : myList) {
freq.compute(str, (k,v)->v == null ? 1 : v + 1);
}
Map<String,List<String>> dupsAndNonDups = new HashMap<>();
for (Entry<String,Long> e : freq.entrySet()) {
dupsAndNonDups.computeIfAbsent(e.getValue() > 1 ? "dups" : "nondups",
k-> new ArrayList<>()).add(e.getKey());
}
System.out.println("dups = " + dupsAndNonDups.get("dups"));
Prints
dups = [ABC]
nondups = [RJL, EFG, IJK]
I have a scenario where i have a list as below :
List<String> a1 = new ArrayList<String>();
a1.add("1070045028000");
a1.add("1070045028001");
a1.add("1070045052000");
a1.add("1070045086000");
a1.add("1070045052001");
a1.add("1070045089000");
I tried below to find duplicate elements but it will check whole string instead of partial string(first 10 digits).
for (String s:al){
if(!unique.add(s)){
System.out.println(s);
}
}
Is there any possible way to identify all duplicates based upon the first 10 digits of a number & then find the lowest strings by comparing from the duplicates & add in to another list?
Note: Also there will be only 2 duplicates with each 10 digit string code always!!
You may group by a (String s) -> s.substring(0, 10)
Map<String, List<String>> map = list.stream()
.collect(Collectors.groupingBy(s -> s.substring(0, 10)));
map.values() would give you Collection<List<String>> where each List<String> is a list of duplicates.
{
1070045028=[1070045028000, 1070045028001],
1070045089=[1070045089000],
1070045086=[1070045086000],
1070045052=[1070045052000, 1070045052001]
}
If it's a single-element list, no duplicates were found, and you can filter these entries out.
{
1070045028=[1070045028000, 1070045028001],
1070045052=[1070045052000, 1070045052001]
}
Then the problem boils down to reducing a list of values to a single value.
[1070045028000, 1070045028001] -> 1070045028000
We know that the first 10 symbols are the same, we may ignore them while comparing.
[1070045028000, 1070045028001] -> [000, 001]
They are still raw String values, we may convert them to numbers.
[000, 001] -> [0, 1]
A natural Comparator<Integer> will give 0 as the minimum.
0
0 -> 000 -> 1070045028000
Repeat it for all the lists in map.values() and you are done.
The code would be
List<String> result = map
.values()
.stream()
.filter(list -> list.size() > 1)
.map(l -> l.stream().min(Comparator.comparingInt(s -> Integer.valueOf(s.substring(10)))).get())
.collect(Collectors.toList());
A straight-forward loop solution would be
List<String> a1 = Arrays.asList("1070045028000", "1070045028001",
"1070045052000", "1070045086000", "1070045052001", "1070045089000");
Set<String> unique = new HashSet<>();
Map<String,String> map = new HashMap<>();
for(String s: a1) {
String firstTen = s.substring(0, 10);
if(!unique.add(firstTen)) map.put(firstTen, s);
}
for(String s1: a1) {
String firstTen = s1.substring(0, 10);
map.computeIfPresent(firstTen, (k, s2) -> s1.compareTo(s2) < 0? s1: s2);
}
List<String> minDup = new ArrayList<>(map.values());
First, we add all duplicates to a Map, then we iterate over the list again and select the minimum for all values present in the map.
Alternatively, we may add all elements to a map, collecting them into lists, then select the minimum out of those, which have a size bigger than one:
List<String> minDup = new ArrayList<>();
Map<String,List<String>> map = new HashMap<>();
for(String s: a1) {
map.computeIfAbsent(s.substring(0, 10), x -> new ArrayList<>()).add(s);
}
for(List<String> list: map.values()) {
if(list.size() > 1) minDup.add(Collections.min(list));
}
This logic is directly expressible with the Stream API:
List<String> minDup = a1.stream()
.collect(Collectors.groupingBy(s -> s.substring(0, 10)))
.values().stream()
.filter(list -> list.size() > 1)
.map(Collections::min)
.collect(Collectors.toList());
Since you said that there will be only 2 duplicates per key, the overhead of collecting a List before selecting the minimum is negligible.
The solutions above assume that you only want to keep values having duplicates. Otherwise, you can use
List<String> minDup = a1.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(s -> s.substring(0, 10), Function.identity(),
BinaryOperator.minBy(Comparator.<String>naturalOrder())),
m -> new ArrayList<>(m.values())));
which is equivalent to
Map<String,String> map = new HashMap<>();
for(String s: a1) {
map.merge(s.substring(0, 10), s, BinaryOperator.minBy(Comparator.naturalOrder()));
}
List<String> minDup = new ArrayList<>(map.values());
Common to those solutions is that you don’t have to identify duplicates first, as when you want to keep unique values too, the task reduces to selecting the minimum when encountering a minimum.
While I hate doing your homework for you, this was fun. :/
public static void main(String[] args) {
List<String> al=new ArrayList<>();
al.add("1070045028000");
al.add("1070045028001");
al.add("1070045052000");
al.add("1070045086000");
al.add("1070045052001");
al.add("1070045089000");
List<String> ret=new ArrayList<>();
for(String a:al) {
boolean handled = false;
for(int i=0;i<ret.size();i++){
String ri = ret.get(i);
if(ri.substring(0, 10).equals(a.substring(0,10))) {
Long iri = Long.parseLong(ri);
Long ia = Long.parseLong(a);
if(ia < iri){
//a is smaller, so replace it in the list
ret.set(i, a);
}
//it was a duplicate, we are done with it
handled = true;
break;
}
}
if(!handled) {
//wasn't a duplicate, just add it
ret.add(a);
}
}
System.out.println(ret);
}
prints
[1070045028000, 1070045052000, 1070045086000, 1070045089000]
Here's another way to do it – construct a Set and store just the 10-digit prefix:
Set<String> set = new HashSet<>();
for (String number : a1) {
String prefix = number.substring(0, 10);
if (set.contains(prefix)) {
System.out.println("found duplicate prefix [" + prefix + "], skipping " + number);
} else {
set.add(prefix);
}
}
I'm trying to refactor this method to use a lambda expression:
public List<String> getHttpsLinksFromCsvList() {
List<String> data = getDataFromCsv();
List<String> httpLinks = new ArrayList<>();
data.forEach(System.out::println);
for (String value : data) {
String[] arrayString = value.split(COMMA_DELIMITER);
for (String item : arrayString) {
if (item.endsWith(".git")) {
httpLinks.add(item);
}
}
}
//httpLinks.forEach(System.out::println);
return httpLinks;
}
Ideally I want to get remove the two nested for loops and optimise it a bit. Is it possible?
Try this:
List<String> httpLinks = getDataFromCsv().stream()
.map(value -> value.split(COMMA_DELIMITER))
.flatMap(Arrays::stream)
.filter(item -> item.endsWith(".git"))
.collect(Collectors.toList());
I am trying to add quotes to data in a CSV file. Below is the approach i have done it. I am sure there is a simpler way using regex or other methods. Would like to know that.
public List<String> addQuotes2List(List<String> list, String delimiter){
List<String> tempList = new ArrayList<>();
String temp="", value;
Integer i=-1, j=0;
for(String s1: list){
//println("S1 - "+s1+" - "+Arrays.asList(s1.split("\\"+delimiter)) );
i++;
tempList = Arrays.asList(s1.split("\\"+delimiter));
//println(tempList);
temp="";j=0;
for(String s2:tempList){
if(j>0)
temp+=delimiter;
//println("S2 - "+s2);
temp+="\""+s2+"\"";
j++;
}
list.set(i, temp);
}
return list;
}
Input
tempList.clear();
tempList.add("Sushanth.Bobby.Lloyds");
tempList.add("Watch.a.lot.of.movies");
tempList.add("main.hobby.is.programming");
tempList.add("programming.is.dangerous.addiction.of.all");
tempList = a.addQuotes2List(tempList,".");
println("tempList - "+tempList.size());
for(String s:tempList)
println(s);
output
tempList - 4
"Sushanth"."Bobby"."Lloyds"
"Watch"."a"."lot"."of"."movies"
"main"."hobby"."is"."programming"
"programming"."is"."dangerous"."addiction"."of"."all"
Thanks,
Sushanth
if you just handle the string .
you may just replce <.> to <"."> and append <"> on starts and ends .
public List<String> addQuotes2List2(List<String> list, String delimiter) {
List<String> tempList = new ArrayList<String>();
// null check list and delimiter
String rStr = "\""+delimiter+"\"";
String rmsg = "";
for (String s1 : list) {
rmsg = s1.replace(delimiter, rStr);
rmsg = "\""+rmsg+"\"";
tempList.add(rmsg);
}
return tempList;
}
regex may no necessary here. (replace and replaceAll made by regex)
If you're using Java 8, you can use streams to make it more readable.
public List<String> addQuotes2List(List<String> list, String delimiter){
return list.stream()
.map(line -> line.split("\\"+delimiter))
.map(this::addQuotes)
.map(entries -> String.join(delimiter, entries))
.collect(Collectors.toList());
}
private List<String> addQuotes(String[] entries) {
return Arrays.stream(entries)
.map(entry -> String.format("%s", entry))
.collect(Collectors.toList());
}