3

I have the following nested groupingBy block:

Map<String,Map<String,Long>> namesCountersMap =
            events.stream().collect(
                Collectors.groupingBy(
                    namesDAO::getName,
                    Collectors.groupingBy(
                        genericDAO::SOME_DYNAMIC_FIELD,
                        Collectors.counting())
                )
            );

There is a situation where I need to call this block 3 times, and the only thing I change is the inner groupingBy field ("SOME_DYNAMIC_FIELD").

Basically what i'm trying to do, is to use group-by and counting for a different field (at the second level) every time, and then merge the results.

Example:

{
      "NamesRecords": {
        "Sam": {
            "Cars": 4
            "Bags": 6 
            "Houses": 2 
        },

        "Bob": {
            "Cars": 2
            "Bags": 1 
            "Houses": 3 
        },

      }
}

As you can see, group-by at the 2nd level is done by 3 different fields, that's is why I need to duplicate this block of code three times.

If I pass a parameter of fieldToGroupBy to the function, is there a way I can use it dynamically? Or if you have any other ideas of how to avoid code duplication, I would love to hear.

2 Answers 2

3

The first argument to Collectors.groupingBy is a Function<T,R>, so you can use a variable of that type.

It is simply a functional interface with a method for extracting the value to group by, and it can be implemented by a method reference, a lambda expression, an anonymous class, or any other type of class.

Function<namesDAO, String> classifier2 = genericDAO::SOME_DYNAMIC_FIELD;

Map<String,Map<String,Long>> namesCountersMap =
        events.stream().collect(
            Collectors.groupingBy(
                namesDAO::getName,
                Collectors.groupingBy(
                    classifier2,
                    Collectors.counting())
            )
        );

Now you can assign different values to classifier2, e.g.

Function<namesDAO, String> classifier2 = someDAO::getCars;

Function<namesDAO, String> classifier2 = otherDAO::getBags;

Function<namesDAO, String> classifier2 = dao -> dao.getHouses();
Sign up to request clarification or add additional context in comments.

Comments

0
public class CarDetailsService {

    private final CarRepository carRepository;

    private final Map<String, Function<CarDTO, String>> carColumnMapper = new HashMap<>();

    public ApplicationDetailsServiceImpl(CarRepository carRepository) {
        this.carRepository = carRepository;

       //---- Initialise all the mappings ------- //

        carColumnMapper.put("BRAND", CarDTO::getBrandName);
        carColumnMapper.put("MILEAGE", CarDTO::getMileage);
    }

    public Map<String, List<CarDTO>> getListOfCars(String groupBy) {

        return carRepository.findAll()
                .stream()
                .map(toCarDTO)
                .collect(groupingBy(carColumnMapper.get(groupBy.toUpperCase())));
    }

    Function<CarDetails, CarDTO> toCarDTO = (carDetails) -> CarDTO
            .builder()
            .brand(carDetails.getBrand())
            .engineCapacity(carDetails.getEngineCapacity())
            .mileage(carDetails.getMileage())
            .fuel(carDetails.getFuel())
            .price(carDetails.getPrice())
            .build();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.