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());
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());
How to change the below code to remove if-else and use Java8
List<String> list1;
List<String> list2;
List<String> list3;
String str;
if(list1.contains(str)){
event.getMessage().setInvocationProperty("ABC","ABC1");
}
else if(list2.contains(str)){
event.getMessage().setInvocationProperty("ABC2","ABC3");
}
else if(list3.contains(str)){
event.getMessage().setInvocationProperty("ABC4","ABC5");
}
Here is how to do it by creating what I'll call a HoldingObject, but you can name it with something more close to your business.
I'm using Lombok's #Value annotation and also java-9's List#of factory method
#Value
public static class HoldingObject {
List<String> list;
String invocationProperty1;
String invocationProperty2;
public void setInvocationPropertyFor(Event event) {
event.getMessage().setInvocationProperty(invocationProperty1, invocationProperty2);
}
}
Note that doing event.getMessage() repeatedly might not be thread-safe if event is accessed through multiple threads
HoldingObject firstObject = new HoldingObject(list1, ABC, ABC1);
HoldingObject secondObject = new HoldingObject(list1, ABC2, ABC3);
HoldingObject thirdObject = new HoldingObject(list1, ABC4, ABC5);
List.of(firstObject, secondObject, thirdObject)
.stream()
.filter(object -> object.getList().contains(str))
.findFirst()
.ifPresent(h -> h.setInvocationPropertyFor(event));
It is possible to do it without if-else, but for this case, if-else would still be better than using streams.
List<String> list1;
List<String> list2;
List<String> list3;
String str;
Map<List<String>, List<Param>> paramMap = new HashMap<>();
paramMap.put(list1,List.of(ABC,ABC1));
paramMap.put(list2,List.of(ABC2,ABC3));
paramMap.put(list3,List.of(ABC4,ABC5));
List.of(list1,list2,list3)
.stream()
.filter(list -> list.contains(str))
.findFirst()
.ifPresent(list -> event.getMessage().setInvocationProperty(paramMap.get(list).get(0),paramMap.get(list).get(1)));
Another solution without using the list as key in paramMap:
Map<Integer, List<Param>> paramMap = new HashMap<>();
paramMap.put(1,List.of(ABC,ABC1));
paramMap.put(2,List.of(ABC2,ABC3));
paramMap.put(3,List.of(ABC4,ABC5));
List<List<String>> lists = List.of(list1,list2,list3);
List<String> mList = lists.stream()
.filter(list -> list.contains(str))
.findFirst()
.ifPresent(list -> {
Integer index = Integer.valueOf(lists.indexOf(list));
event.getMessage()
.setInvocationProperty(paramMap.get(index).get(0),paramMap.get(index).get(1))
});
I have the following List of String:
{
"Name1,Name2",
"Name2,Name1",
"Name3,Name4",
"Name4,Name3"
}
Without using any Java/C/Python/C++/C# library, I want to remove the duplicates in a way that, it prints:
Name1,Name2
Name3,Name4
One way to remove duplicates would be this:
private static boolean checkIfEquals(String str, String str1) {
HashSet<String> set1 = new HashSet<>(Arrays.asList(str.split(",")));
HashSet<String> set2 = new HashSet<>(Arrays.asList(str1.split(",")));
return set1.equals(set2);
}
Using your same approach, assuming your list of strings is in a variable List<String> strings:
List<String> unique =
strings.stream()
.map(str -> new LinkedHashSet<>(Arrays.asList(str.split(","))))
.distinct()
.map(set -> set.stream().collect(Collectors.joining(",")))
.collect(Collectors.toList());
using c++
int main(){
ios_base::sync_with_stdio(false);
string list[4]={
"Name1,Name2",
"Name2,Name1",
"Name3,Name4",
"Name4,Name3"
};
map<string,bool>exist;
vector<string>flist;
for(int i=0;i<4;i++){
string s=list[i];
sort(s.begin(),s.end());
if(exist[s])continue;
exist[s]=true;
flist.push_back(list[i]);
}
for(auto item :flist)cout<<item<<"\n";
}
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(",")));
}
I have an example here which basically returns list based on simple logic
Given an input list and a list of grouping objects, which has a list field, the method should return a list that contains either all the members of grouping.list if the grouping.name matches any of the strings in the input list OR simply add the input string to the returning list.
After I writing this code, I am thinking it could be made simpler in Java 7 and a better example to use Java 8 Streaming API.
public class CollectorExample {
public static void main(String[] args){
List<String> input = new ArrayList<>();
input.add("foo");
input.add("bar");
input.add("foobar");
input.add("java");
List<String> list1 = new ArrayList<>();
list1.add("hello");
list1.add("world");
List<String> list2 = new ArrayList<>();
list2.add("spring");
list2.add("multi-threaded");
Grouping g1 = new Grouping("foobar",list1);
Grouping g2 = new Grouping("java",list2);
List<Grouping> groupingList = new ArrayList<>();
groupingList.add(g1);
groupingList.add(g2);
System.out.println(mapAndMerge(input,groupingList));
}
public static List<String> mapAndMerge(List<String> input, List<Grouping> groupingList){
Set<String> returnDocs = new HashSet<>();
Iterator<String> it = input.iterator();
while(it.hasNext()){
String doc = it.next();
boolean found = false;
for (Grouping lg : groupingList){
if (lg.getName().equals(doc)){
returnDocs.addAll(lg.getList());
found=true;
}
}
if (!found){
returnDocs.add(doc);
}
}
return new ArrayList<>(returnDocs);
}
}
class Grouping {
List<String> list;
String name;
public Grouping(String name, List<String> list){
this.list=list;
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
This outputs [spring, bar, world, foo, hello, multi-threaded] which is correct.
Here is my Java 8 syntax that I tried and did NOT work;
// List<String> mergedDocs =
// input.forEach(doc->
// groupingList.stream().map( g -> g.getName().equals(doc) ? e.getList() : doc ).collect(Collectors.toList()));
// return mergedDocs;
You can make this a lot simpler by not using your Grouping class but using a simple Map<String, List<String>> instead. This map would act as the grouping, holding the list for a given name. This also enables to have a much better performance since looking into the map is constant-time (whereas your solution is in linear time since it traverses the grouping to find a matching one).
If you have to use the List<Grouping>, you can still pre-process it to convert into an intermediate Map:
The mapAndMerge method simply becomes:
public static List<String> mapAndMerge(List<String> input, List<Grouping> groupingList) {
Map<String, List<String>> map = groupingList.stream().collect(Collectors.toMap(Grouping::getName, Grouping::getList));
return input.stream()
.flatMap(s -> map.getOrDefault(s, Arrays.asList(s)).stream())
.collect(Collectors.toList());
}
Each input is flat mapped to the list contained in the map or a default list containing the current element. Then this is collected to a new list. This code prints:
[foo, bar, hello, world, spring, multi-threaded]
You can re-write the mapAndMerge method following way using java 8. But it is not very concise as you like.
public static List<String> mapAndMerge(List<String> input,
List<Grouping> groupingList) {
Set<String> returnDocs = input
.stream()
.map(t -> groupingList
.stream()
.filter(g -> g.getName().equals(t))
.map(v -> v.getList())
.findAny()
.orElse(Arrays.asList(t)))
.flatMap(t -> t.stream())
.collect(Collectors.toSet());
return new ArrayList<>(returnDocs);
}
I think it would be much simple and clearer if you use Map instead of the Grouping class.
So that's what you'll have in the main() method:
Map<String, List<String>> groupingMap = new HashMap<>();
groupingMap.put("foobar", list1);
groupingMap.put("java", list2);
List<String> mergedDocs = new ArrayList<>();
input.stream()
.map(doc -> groupingMap.getOrDefault(doc, Collections.singletonList(doc)))
.forEach(mergedDocs::addAll);
System.out.println(mergedDocs);