Skip to content
Advertisement

Sort collection according to user custom input by single or multiple attributes

I have collection of Item object and the class Item is as follows ->

class Item {
  String id;
  String brand;
  Integer price;
  Date publishedDate;
}

Now, I want to sort according to the user input. User can sort it according to his choice like brand, price or publishedDate and even with the multiple parameters. Like brand and price or price and publishedDate.

I have seen Comparator to sort by multiple parameters but that also limited and with predefined parameters. So basically user can sort by any input and sorting mechanism can handle that without changing any code.

I get some hints to use strategic design pattern but not able to implement here to resolve it.

Answer

You could use a static Map for a Comparator for each field and build a combined Comparator out of that: You’d still have to make the comparators null-safe and check the field names…

class Item {
    String id;
    String brand;
    Integer price;
    Date publishedDate;

    public Item(String id, String brand, Integer price, Date publishedDate) {
        super();
        this.id = id;
        this.brand = brand;
        this.price = price;
        this.publishedDate = publishedDate;
    }

    static Map<String, Comparator<Item>> comparatorMap = new HashMap<String, Comparator<Item>>() {
        {
            put("id", new Comparator<Item>() {
                @Override
                public int compare(Item o1, Item o2) {
                    return o1.id.compareTo(o2.id);
                }
            });
            put("brand", new Comparator<Item>() {
                @Override
                public int compare(Item o1, Item o2) {
                    return o1.brand.compareTo(o2.brand);
                }
            });
            put("price", new Comparator<Item>() {
                @Override
                public int compare(Item o1, Item o2) {
                    return o1.price.compareTo(o2.price);
                }
            });
            put("publishedDate", new Comparator<Item>() {
                @Override
                public int compare(Item o1, Item o2) {
                    return o1.publishedDate.compareTo(o2.publishedDate);
                }
            });
        }
    };

    @Override
    public String toString() {
        return "Item [id=" + id + ", brand=" + brand + ", price=" + price + ", publishedDate=" + publishedDate + "]";
    }

    static class ItemComparator implements Comparator<Item> {
        private List<Comparator<Item>> comparators;

        ItemComparator(String... fields) {
            comparators = new ArrayList<>();
            for (String field : fields) {
                comparators.add(comparatorMap.get((field)));
            }
        }

        @Override
        public int compare(Item o1, Item o2) {
            int result = 0;
            for (Comparator comparator : comparators) {
                result = comparator.compare(o1, o2);
                if (result != 0) {
                    return result;
                }
            }
            return result;
        }

    }

    static List<Item> sort(List<Item> list, String... fields) {
        Collections.sort(list, new ItemComparator(fields));
        return list;
    }

    public static void main(String[] args) {
        List<Item> list = new ArrayList<>();
        list.add(new Item("B", "A", 5, new Date()));
        list.add(new Item("A", "B", 7, new Date()));

        sort(list, "id", "price");
        System.out.println(list);
    }
}

EDIT

Version using comparing / thencomparing

The map could be omitted when using reflection to get the getters…

class Item {
    String id;
    String brand;
    Integer price;
    Date publishedDate;

    public Item(String id, String brand, Integer price, Date publishedDate) {
        super();
        this.id = id;
        this.brand = brand;
        this.price = price;
        this.publishedDate = publishedDate;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    public Date getPublishedDate() {
        return publishedDate;
    }

    public void setPublishedDate(Date publishedDate) {
        this.publishedDate = publishedDate;
    }

    static Map<String, Comparator<Item>> comparatorMap = new HashMap<String, Comparator<Item>>() {
        {
            put("id", Comparator.comparing(Item::getId));
            put("brand", Comparator.comparing(Item::getBrand));
            put("price", Comparator.comparing(Item::getPrice));
            put("publishedDate", Comparator.comparing(Item::getPublishedDate));
        }
    };

    static List<Item> sort(List<Item> list, String... fields) {
        Comparator comparator = null;
        for (String field : fields) {
            comparator = (comparator == null) ?  comparatorMap.get((field))
                : comparator.thenComparing(comparatorMap.get((field)));
        }

        if (comparator != null) {
            Collections.sort(list, comparator);
        }
        return list;
    }

    @Override
    public String toString() {
        return "Item [id=" + id + ", brand=" + brand + ", price=" + price + ", publishedDate=" + publishedDate + "]";
    }

    public static void main(String[] args) {
        List<Item> list = new ArrayList<>();
        list.add(new Item("B", "A", 5, new Date()));
        list.add(new Item("A", "B", 7, new Date()));

        sort(list, "id", "price");
        System.out.println(list);
    }
}
Advertisement