Skip to content
Advertisement

Java Filter one stream based on another stream and collect

I have this code working with a for loop and steam, but I was wondering I can improve it using two streams. Here I want to replace the for loop with a stream and would like to get a list of String that the method returns.

public class City {
    
    final int x;
    final int y;
    final String name;
    public City(String name, int x, int y) {
        this.name = name;
        this.x = x;
        this.y = y;
    }
    public int getX() {
        return x;
    }
    public int getY() {
        return y;
    }
    public String getName() {
        return name;
    }
}



import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
        public class Test {
        
            public static boolean cityFilter(City city, City c) {
                return !city.getName().equals(c.getName()) && (city.getX()==c.getX() || city.getY()==c.getY())?true:false;
            }
        
            public static int compareCities(City c, City c1, City c2) {
                int dis1 = Math.abs(c.getX() - c1.getX()) + Math.abs(c.getY() - c1.getY());
                int dis2 = Math.abs(c.getX() - c2.getX()) + Math.abs(c.getY() - c2.getY());
                return dis1==dis2?c.getName().compareTo(c2.getName()):dis1-dis2;
            }
            
            public static List<String> filterAndFindCities(List<String> c, List<Integer> x, List<Integer> y, List<String> q) {
                Map<String, City> cityMap = IntStream.range(0, c.size()).boxed().collect(Collectors.toMap(c::get, i-> new City(c.get(i), x.get(i), y.get(i))));
                List<String> rst = new ArrayList<String>();
    //====How can I replace this for loop with a stream and get a list of String?===
                for (String s : q) {
                    City givenCity = cityMap.get(s);
                    City nearest = cityMap.values().stream().filter(p -> cityFilter(givenCity, p))
                                            .sorted((c1, c2) -> compareCities(givenCity, c1, c2))
                                            .findFirst().orElse(null);
                    String nearestCity = nearest != null ? nearest.getName() : "EMPTY";
                    rst.add(nearestCity);
                }
                return rst;
            }
        }

Advertisement

Answer

I was able to find a quick and dirty solution, of course not the best, but sure I can improve this.

public static List<String> filterAndFindCities(List<String> c, List<Integer> x, List<Integer> y, List<String> q) {

Map<String, City> cityMap = IntStream.range(0, c.size()).boxed()
                            .collect(Collectors
                            .toMap(c::get, i -> new City(c.get(i), x.get(i), 
                                                                    y.get(i))));
return q.stream()
       .map(name ->cityMap.values().stream()
         .filter(p -> cityFilter(cityMap.get(name), p))
         .sorted((c1, c2) -> compareCities(cityMap.get(name), c1, c2))
         .findFirst().orElse(null))
       .map(p->p!=null?p.getName():"EMPTY")
       .collect(Collectors.toList());
}
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement