user2248702
user2248702

Reputation: 2988

Sort list based on separate list

I have a list of objects with a method getPriority() which returns the objects priority in a string and a method getName() which returns the objects name (also a string). I have a list, 'priority', which contains strings like 'high' 'medium' and 'low'.

I would like to order the list of objects by the position of the getPriority() method in the order list and for objects which have the same priority or no priority then order them alphabetically based on name.

For example:

    List<MyObject> objects = new ArrayList<MyObject>();
    objects.add(new MyObject("apple", "low"));
    objects.add(new MyObject("bacon", "medium"));
    objects.add(new MyObject("cheese", "low"));
    objects.add(new MyObject("doughnut", "high"));
    objects.add(new MyObject("eggs", null));
    objects.add(new MyObject("fudge", null));

    List<String> priority = new ArrayList<String>();
    priority.add("high");
    priority.add("medium");
    priority.add("low");

would be sorted as: doughnut, bacon, apple, cheese, eggs, fudge.

It is important to not use an enum for priority as it needs to be customisable by the end user. It also needs to accept a null priority as priority is taken from a database and the objects still need to be sorted if not found in the database.

Upvotes: 2

Views: 855

Answers (3)

Jeff
Jeff

Reputation: 3882

I would recommend using Guava Ordering.

Using Ordering.explicit allows us to compare things in the order that they appear in a list.

In order to sort the objects by name after they are sorted by priority, the two orderings are composed together with Ordering.thenComparing.

// Sorts MyObject by name
public static final Ordering<MyObject> MY_OBJECT_FOOD_ORDERING = new Ordering<MyObject>() {

    @Override
    public int compare(final MyObject left, final MyObject right) {

        return ComparisonChain.start()
                .compare(left == null ? null : left.getName(), right == null ? null : right.getName())
                .result();
    }
};

public static final Ordering<MyObject> buildPriorityOrdering(final List<String> priorities) {

    return buildPriorityOrdering(Ordering.explicit(ImmutableList.<String> builder()
            .addAll(priorities)
            .build())
        .nullsLast());
}

public static final Ordering<MyObject> buildPriorityOrdering(final Ordering<String> priorityOrdering) {
    return new Ordering<ListOrdering.MyObject>() {

        @Override
        public int compare(final MyObject left, final MyObject right) {

            return ComparisonChain.start()
                .compare(left == null ? null : left.getPriority(), right == null ? null : right.getPriority(),
                        priorityOrdering)
                .result();
        }
    };
}

public static void main(final String... args) {

    final List<String> priorities = ImmutableList.<String> builder()
            .add("high")
            .add("medium")
            .add("low")
            .build();

    final List<MyObject> myObjects = ImmutableList.<MyObject> builder()
            .add(new MyObject("doughnut", "high"))
            .add(new MyObject("cheese", "low"))
            .add(new MyObject("eggs", null))
            .add(new MyObject("apple", "low"))
            .add(new MyObject("fudge", null))
            .add(new MyObject("bacon", "medium"))
            .build();

    final List<MyObject> myObjectsSorted = FluentIterable.from(myObjects)
            .toSortedList(buildPriorityOrdering(priorities).thenComparing(MY_OBJECT_FOOD_ORDERING));

    /*
     * Results: 
     * MyObject [name=doughnut, priority=high] 
     * MyObject [name=bacon, priority=medium] 
     * MyObject [name=apple, priority=low] 
     * MyObject [name=cheese, priority=low] 
     * MyObject [name=eggs, priority=null] 
     * MyObject [name=fudge, priority=null]
     */

    System.out.println(Joiner.on("\n")
            .join(myObjectsSorted));
}

public static class MyObject {

    private final String name;

    private final String priority;

    public MyObject(final String name, final String priority) {
        this.name = name;
        this.priority = priority;
    }

    public String getName() {
        return this.name;
    }

    public String getPriority() {
        return this.priority;
    }

    @Override
    public String toString() {
        return "MyObject [name=" + this.name + ", priority=" + this.priority + "]";
    }
}

Thanks @Barett for nullsFirst nullsLast

Upvotes: 1

Barett
Barett

Reputation: 5948

Add a new comparator class to sort your objects, defined as follows:

class MyObjectPrioritizer extends Comparator<MyObject> {
    List<String> priority;

    MyObjectPrioritizer(List<String> priorityOrder) {
        priority = priorityOrder;
    }

    public int compare(MyObject o1, MyObject o2) {
        int v1 = resolveValue(o1);
        int v2 = resolveValue(o2);
        return v1 - v2;
    }

    int resolveValue(MyObject o) {
        int sort = priority.indexOf(o.priority));
        // put null & unknown values at the end
        if (sort == -1) sort = Integer.MAX_VALUE;
        return sort;
    }
}

Then just:

Collections.sort(objects, new MyObjectPrioritizer(priority));

Upvotes: 2

Paul Vargas
Paul Vargas

Reputation: 42010

You can use a comparator for sort the list. e.g.:

Code:

List<MyObject> objects = new ArrayList<MyObject>();
objects.add(new MyObject("apple", "low"));
objects.add(new MyObject("bacon", "medium"));
objects.add(new MyObject("cheese", "low"));
objects.add(new MyObject("doughnut", "high"));
objects.add(new MyObject("eggs", null));
objects.add(new MyObject("fudge", null));

final List<String> priority = new ArrayList<String>();
priority.add("high");
priority.add("medium");
priority.add("low");

Comparator<MyObject> comparator = new Comparator<MyObject>() {
    public int compare(MyObject o1, MyObject o2) {
        int p1 = priority.indexOf(o1.getPriority());
        int p2 = priority.indexOf(o2.getPriority());
        if (p1 == -1 && p2 != -1) {
            return 1;
        }
        if (p1 != -1 && p2 == -1) {
            return -1;
        }
        if (p1 != p2) {
            return p1 - p2;
        }
        return o1.getName().compareTo(o2.getName());
    }
};

Collections.sort(objects, comparator);

for(MyObject obj : objects) {
    System.out.printf("%-10s %10s%n", obj.getName(), obj.getPriority());
}

Output:

doughnut         high
bacon          medium
apple             low
cheese            low
eggs             null
fudge            null

Upvotes: 1

Related Questions