Reputation: 113
I've made a small Spring boot service that allows the ability to filter and sort a list of objects, with the sorting able to be defined in any order.
The object only has a few values within it: name (string), height (double), category (string), and grid reference (string)
I have converted the API arguments for sorting into a list of String arguments. An example would be ["category, "desc", height, "asc"] "Sort by category descending, and then height ascending"
Given how few values there are I decided to first get my test passing by using a switch statement by matching the string values to an enum.
The solution code
List<Munro> findData(MunroRequest munroRequest) {
List<Munro> munroData = munroRepository.find();
Stream<Munro> munroStream = munroData.stream().filter(munro -> munro.getHeight() <= munroRequest.getMaxHeight())
.filter(munro -> munro.getHeight() >= munroRequest.getMinHeight());
if(!munroRequest.getCategoryFilter().equals(MunroCategoryFilter.ALL)) {
munroStream = munroStream.filter(munro -> munro.getCategory().equals(munroRequest.getCategoryFilter().name()));
}
munroStream = sort(munroRequest, munroStream);
return munroStream.limit(munroRequest.getMaxResults()).collect(Collectors.toList());
}
private Stream<Munro> sort(MunroRequest munroRequest, Stream<Munro> munroStream) {
Comparator<Munro> munroComparator = Comparator.comparing(Munro::getGridReference);
for (int i = 0; i < munroRequest.getMunroSorts().size() ; i++ ) {
switch (munroRequest.getMunroSorts().get(i).getFieldToSort()) {
case NAME:
munroComparator = munroComparator.thenComparing(Munro::getName);
break;
case HEIGHT:
munroComparator = munroComparator.thenComparing(Munro::getHeight);
break;
case CATEGORY:
munroComparator = munroComparator.thenComparing(Munro::getCategory);
break;
case GRID_REFERENCE:
munroComparator = munroComparator.thenComparing(Munro::getGridReference);
break;
default:
throw new RuntimeException();
}
if(!munroRequest.getMunroSorts().get(i).isAscending()) {
munroComparator = munroComparator.reversed();
}
}
return munroStream.sorted(munroComparator);
}
My test code for sorting:
@Test
void sortResults() {
MunroRequest sortRequest = new MunroRequest.Builder().withSorting(Arrays.asList(MunroSortingFields.CATEGORY.getParam, "desc", MunroSortingFields.HEIGHT.getParam, "asc")).build();
List<Munro> munros = makeMunros();
when(mockedRepository.find()).thenReturn(munros);
List<Munro> sortedMunro = munroService.findData(sortRequest);
assertEquals("TOP", sortedMunro.get(0).getCategory());
assertEquals(45.5D, sortedMunro.get(1).getHeight());
assertEquals(45.6D, sortedMunro.get(2).getHeight());
assertEquals(60.6D, sortedMunro.get(3).getHeight());
}
private List<Munro> makeMunros() {
Munro otherMunroToFilter = new Munro("Charlie", 45.6D, "MUN", "001233");
Munro munroToInclude = new Munro("Sam", 45.5D, "MUN", "001234");
Munro munroTOFilter = new Munro("Bill", 60.5D, "TOP", "001235");
Munro largestMunro = new Munro("Dillinger", 60.6D, "MUN", "001232");
return Arrays.asList(munroToInclude, munroTOFilter, otherMunroToFilter, largestMunro);
}
Whole code can be found here but I think I've included what's important.
The thing is I'm really unhappy with this solution. I've been trying to look for a solution where I could avoid a switch statement as the information of mapping these arguments to fields is done in quite a verbose way.
I was hoping some sort of mapping of string to comparator function could be done elsewhere but haven't figured out what I could do. Using Reflection would acceptable but ideally avoided but I'm not sure how that could be done either.
I don't really make many questions but I genuinely couldn't find any similar question to this with a variable set of sorting criteria.
Upvotes: 1
Views: 529
Reputation: 17460
You can add the mapping function to the Enumeration and thus get rid of the switch statement.
You MunroSortingFields
enum would become:
public enum MunroSortingFields {
NAME("name", Munro::getName),
HEIGHT("height", Munro::getHeight),
CATEGORY("category", Munro::getCategory),
GRID_REFERENCE("grid_reference", Munro::getGridReference);
String getParam;
Function<? super Munro, ? extends Comparable> comparingFunction;
MunroSortingFields(String getParam, Function<? super Munro, ? extends Comparable> comparingFunction) {
this.getParam = getParam;
this.comparingFunction = comparingFunction;
}
public static Optional<MunroSortingFields> fromString(String getParam) {
return Arrays.stream(MunroSortingFields.values()).filter(field -> field.getParam.equals(getParam)).findFirst();
}
}
And your sort()
method would become way simpler:
private Stream<Munro> sort(MunroRequest munroRequest, Stream<Munro> munroStream) {
Comparator<Munro> munroComparator = Comparator.comparing(Munro::getGridReference);
for (int i = 0; i < munroRequest.getMunroSorts().size() ; i++ ) {
munroComparator = munroComparator.thenComparing(munroRequest.getMunroSorts().get(i).getFieldToSort().comparingFunction);
if(!munroRequest.getMunroSorts().get(i).isAscending()) {
munroComparator = munroComparator.reversed();
}
}
return munroStream.sorted(munroComparator);
}
Upvotes: 1