Reputation: 471
I have this method which works fine but is there any cleaner way to do it?
Currently I have added comparators to the map and get the right comparator based on user-selected value.
private Comparator<? super BusinessPartnerAssignmentDetail> getComparator(PortfolioFilterDto portfolioFilterDto){
Map<String, Comparator<? super BusinessPartnerAssignmentDetail>> sortingOptions = new HashMap<>();
sortingOptions.put("fieldOfficeDescription", Comparator.comparing(BusinessPartnerAssignmentDetail::getFieldOfficeDescription, Comparator.nullsLast(Comparator.naturalOrder())));
sortingOptions.put("locationDescription", Comparator.comparing(BusinessPartnerAssignmentDetail::getLocationDescription, Comparator.nullsLast(Comparator.reverseOrder())));
sortingOptions.put("segmentType", Comparator.comparing(BusinessPartnerAssignmentDetail::getSegmentType, Comparator.nullsLast(Comparator.reverseOrder())));
sortingOptions.put("displayName", Comparator.comparing(BusinessPartnerAssignmentDetail::getDisplayName, Comparator.nullsLast(Comparator.reverseOrder())));
return sortingOptions.get(portfolioFilterDto.getSortParameter());
}
And then I call the sort on my list like this
businessPartnerAssignmentDetails.sort(getComparator(portfolioFilterDto));
Upvotes: 1
Views: 118
Reputation: 15008
You can wrap all this in an enum
, like Sourin says, but you can enrich it with more functionality like actually comparing.
enum AssignmentFilter implements Comparator<BusinessPartnerAssignmentDetail> {
fieldOfficeDescription(BusinessPartnerAssignmentDetail::getFieldOfficeDescription, Comparator.naturalOrder()),
locationDescription(BusinessPartnerAssignmentDetail::getLocationDescription),
segmentType(BusinessPartnerAssignmentDetail::getSegmentType),
displayName(BusinessPartnerAssignmentDetail::getDisplayName);
private Function<BusinessPartnerAssignmentDetail, Comparable<? super BusinessPartnerAssignmentDetail>> compareByKey;
AssignmentFilter(Function<BusinessPartnerAssignmentDetail, Comparable<? super BusinessPartnerAssignmentDetail>> byKey, Comparator<BusinessPartnerAssignmentDetail> whenNull) {
compareByKey = Comparator.comparing(byKey, Comparator.nullsLast(whenNull));
}
AssignmentFilter(Function<BusinessPartnerAssignmentDetail, Comparable<? super BusinessPartnerAssignmentDetail>> byKey) {
this(byKey, Comparator.reverseOrder());
}
public int compare(BusinessPartnerAssignmentDetail bpad1, BusinessPartnerAssignmentDetail bpad1) {
return comparator().compare(bpad1, bpad2);
}
}
You can call this by businessPartnerAssignmentDetails.sort(AssingmentFilter.valueOf(portfolioFilterDto.getSortParameter()))
.
Whether this is better than just filling a map is your call.
Upvotes: 0
Reputation: 823
Try using an ENUM for Sorting options and use a switch case to get the particular Comparator :
DTO with Enum filter options
public class PortfolioFilterDto {
/*your existing code
*
*
* */
PortfolioFilterDtoOptions sortParameter;
enum PortfolioFilterDtoOptions {
fieldOfficeDescription, locationDescription, segmentType, displayName
}
public PortfolioFilterDtoOptions getSortParameter() {
return this.sortParameter;
}
}
Your method to dynamically get filter option
private Comparator<? super BusinessPartnerAssignmentDetail> getComparator(PortfolioFilterDto portfolioFilterDto) {
switch (PortfolioFilterDto.getSortParameter()) {
case PortfolioFilterDtoOptions.fieldOfficeDescription:
return Comparator.comparing(BusinessPartnerAssignmentDetail::getFieldOfficeDescription, Comparator.nullsLast(Comparator.naturalOrder()));
case PortfolioFilterDtoOptions.locationDescription:
return Comparator.comparing(BusinessPartnerAssignmentDetail::getLocationDescription, Comparator.nullsLast(Comparator.reverseOrder()));
case PortfolioFilterDtoOptions.segmentType:
return Comparator.comparing(BusinessPartnerAssignmentDetail::getSegmentType, Comparator.nullsLast(Comparator.reverseOrder()));
case PortfolioFilterDtoOptions.displayName:
return Comparator.comparing(BusinessPartnerAssignmentDetail::getDisplayName, Comparator.nullsLast(Comparator.reverseOrder()));
default:
// put default filter here
return null;
}
}
Upvotes: 0
Reputation: 44476
The only shortcut I see is to automatize the transformation of the String
method representation to the Function<? super BusinessPartnerAssignmentDetail, U>
using reflection.
Important: This solution might be useful in a huuuge number of getters and possible combinations. The getters must be formal parameters free (standard getters). I would prefer to stick with your current solution which is the way more readable and maintainable, which I consider as the priority.
Solution and its description:
Method
from the String
using Class::getMethod
.Function<? super BusinessPartnerAssignmentDetail, U>
from Method
.
Method::invoke
silentInv
) for sake of clarity.static List<String> naturalOrderList = Arrays.asList("fieldOfficeDescription");
static <U extends Comparable<U>> Comparator<? super BusinessPartnerAssignmentDetail>
getComparator(PortfolioFilterDto p) throws NoSuchMethodException
{
/** (1) **/
Method method = BusinessPartnerAssignmentDetail.class.getMethod(p.getSortParameter());
/** (2) **/
Function<? super BusinessPartnerAssignmentDetail, U> function = silentInv(method);
/** (3) **/
Comparator<U> order = methodsWithNaturalOrders.contains(method.getName())
? Comparator.naturalOrder()
: Comparator.reverseOrder();
return Comparator.comparing(function, Comparator.nullsLast(order));
}
@SuppressWarnings("unchecked")
static <U extends Comparable<U>> Function<? super BusinessPartnerAssignmentDetail, U>
silentInv(Method method)
{
/** (2) The necessary try-catch wrapping, the exception should never be thrown **/
return bpad -> {
try {
return (U) method.invoke(bpad);
} catch (Exception e) {
String message = "Invalid method name " + method.getName();
throw new IllegalArgumentException(message , e);
}
};
}
Hint: Use shorter class names, if possible :)
Upvotes: 1
Reputation: 1480
creating a map of comparators each time the method is invoked is a bad idea.
instead you could return the required comparator in a switch case
switch (portfolioFilterDto.getSortParameter()){
case "fieldOfficeDescription" :
return Comparator.comparing(BusinessPartnerAssignmentDetail::getFieldOfficeDescription, Comparator.nullsLast(Comparator.naturalOrder()));
case "locationDescription" :
return Comparator.comparing(BusinessPartnerAssignmentDetail::getLocationDescription, Comparator.nullsLast(Comparator.reverseOrder()));
case "segmentType" :
return Comparator.comparing(BusinessPartnerAssignmentDetail::getSegmentType, Comparator.nullsLast(Comparator.reverseOrder()));
case "displayName" :
return Comparator.comparing(BusinessPartnerAssignmentDetail::getDisplayName, Comparator.nullsLast(Comparator.reverseOrder()));
default :
return null;
}
or you can create the map inside the constructor as a global field, then you wouldn't initialize a map every time it's revoked.
Upvotes: 0