views:

851

answers:

4

Suppose I have a list (or Set):

List<String> testList = Lists.newArrayList("assocX","srcT","destA","srcX", "don't care Y", "garbage", "srcB");

I would like to get back an ImmutableList(Set) that sorts/groups terms in natural order where terms that begin with "src" are first, "assoc" second and "dest" last. If a term does not contain those then it should be removed from the resulting list.

Therefore the result here is "srcB", "srcT", "assocX", "destA".

I think I can do this with some combination of Iterables.filter or Predicates but just not seeing it. There must be a succinct way of doing it I think.

EDIT: A set in place of a list works as well.

+1  A: 

I think you'll have first to use the predicate to eliminate elements you don't want, and the implement a Comparator and sort your list.

Valentin Rocher
A: 

Have a look at This Google Collections example.

Function<Fruit, String> getNameFunction = new Function<Fruit, String>() {
    public String apply(Fruit from) {
        return from.getName();
    }
};

Ordering<Fruit> nameOrdering = Ordering.natural().onResultOf(getNameFunction);

ImmutableSortedSet<Fruit> sortedFruits = ImmutableSortedSet.orderedBy(
    nameOrdering).addAll(fruits).build();

Though this, admittedly, returns a Set.

extraneon
+3  A: 

As long as those three prefixes are the only things you care about, I'd suggest something like this:

    Predicate<String> filter = new Predicate<String>() {
        @Override
        public boolean apply(String input) {
            return input.startsWith("src") || input.startsWith("assoc") || input.startsWith("dest");
        }
    };

    Function<String, Integer> assignWeights = new Function<String, Integer>() {
        @Override
        public Integer apply(String from) {
            if (from.startsWith("src")) {
                return 0;
            } else if (from.startsWith("assoc")) {
                return 1;
            } else if (from.startsWith("dest")) {
                return 2;
            } else {
                /* Shouldn't be possible but have to do something */
                return 3;
            }
        }
    };

    ImmutableList<String> sortedFiltered = ImmutableList.copyOf(
            Ordering.natural().onResultOf(assignWeights).sortedCopy(
                    Iterables.filter(testList, filter)
            )
    );

This solution definitely wouldn't scale out incredibly well if you start adding more prefixes to filter out or sort by, since you'd have to continually update both the filter and the weight of each prefix.

Paul Blessing
A: 

Usually it's bad design to collate clearly distinct data like this. In your case, when you say "assocX", "assoc" has a separate meaning from "X", yet you merge them together.

So I would suggest designing a class with two fields. Then you can create an ordering on the first field, another on the second, and combine them (e.g. Ordering#compound()). With a toString() method that does merge these fields into a string. As a bonus, this may greatly reduce memory usage via sharing.

So you would be sorting a list of such objects, and if you wanted to print them, you would just call toString() on them.

Dimitris Andreou