Search code examples
javalistjava-stream

Filter one Java 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;
    }
}

Solution

  • 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());
    }