Marcel
Marcel

Reputation: 1594

Java - Sort a List of strings, determining the order depending on the contains of the string

Example:

Our list contains 5 names: Kevin, Hans, Fritz, Han Solo, Peter

And I now want to have all the names that contain "Han" at the top.

So the sorted list would look like that:

Hans, Han Solo, Kevin, Fritz, Peter

What I have tried so far:

Nothing because i have no clue, but i already googled and didn't find anything.

Deleting/adding items from/to the list is not an option becaue i am using the list in a CheckListView (ControlsFX component) where each item has a checked state which would get lost.

Upvotes: 4

Views: 8240

Answers (4)

DVarga
DVarga

Reputation: 21799

You can use a Comparator to specify how the sorting should work.

This solution really just moves elements containing "Han" to the front of the list - while keeping the original order between these elements (as it was defined in the desired output) - and it does not sort elements not containing "Han" as the question states: "And I now want to have all the names that contain "Han" at the top."

List<String> names = Arrays.asList("Kevin", "Hans", "Fritz", "Han Solo", "Peter"));

names.sort(getMoveToFrontComparator("Han"));

System.out.println(names);

...

private Comparator<String> getMoveToFrontComparator(String part) {
    return new Comparator<String>() {

        @Override
        public int compare(String o1, String o2) {
            boolean containsFirst = o1.contains(part);
            boolean containsSecond = o2.contains(part);

            // Match vs No-Match: Match has the priority
            if(containsFirst && !containsSecond)
                return -1;
            if(!containsFirst && containsSecond)
                return 1;

            // Match vs Match or No-Match vs No-Match: no sorting is needed
            return 0;
        }
    };
}

The output is:

[Hans, Han Solo, Kevin, Fritz, Peter]

Note: If you want to sort the "matching vs matching" and the "non-matching vs non-matching" case, you can replace return 0; with return o1.compareTo(o2);.

Upvotes: 0

fabian
fabian

Reputation: 82461

In java 8 you can create Comparators based on a function applied to the element. Also there are methods to reverse the order produced by a comparator and chain comparators.

This allows a relatively simple creation of a comparator using the existence of a substring as primary sort criterion and the String sorting as secondary criterion. Note that depending on your requirements the second part (.thenComparing(Comparator.naturalOrder())) may not be necessary:

final String part = "Han";

Comparator<String> comparator = Comparator.<String, Boolean>comparing(s -> s.contains(part)).reversed()
        .thenComparing(Comparator.naturalOrder());

The result of sorting your items with this Comparator is

Han Solo, Hans, Fritz, Kevin, Peter

Upvotes: 7

explv
explv

Reputation: 2759

For this you need to use a Comparator<String>.

When comparing two Strings lets call them name1 and name2, if name1 contains "Han" then you return -1 indicating that name1 should come first. If name2 contains "Han" then you return 1 indicating that name2 should come first. Otherwise neither String contains "Han" so you return 0 which indicates that they should not be swapped. Note: this does not alphabetically sort the Strings that contain "Han" as you did not specify this in your requirements.

In Java 8 this is simple:

List<String> names = Arrays.asList("Kevin", "Hans", "Fritz", "Han Solo", "Peter");

names.sort((name1, name2) ->
   name1.contains("Han") ? -1 :
   name2.contains("Han") ? 1  :
   0
);

System.out.println(names);

Output:

[Han Solo, Hans, Kevin, Fritz, Peter]

Upvotes: 0

Jordi Castilla
Jordi Castilla

Reputation: 26961

just use Comparator and define your own compare method with your conditions:

public static void main(String[] args) {
    List<String> names = Arrays.asList("Kevin,Hans,Fritz,Han Solo,Peter".split(","));

    Collections.sort(names, new Comparator<String>() {
        final String PREFIX = "Han";
        @Override
        public int compare(String a, String b) {
            if (a.contains(PREFIX) && b.contains(PREFIX)) return a.compareTo(b);
            if (a.contains(PREFIX) && !b.contains(PREFIX)) return -1;
            if (!a.contains(PREFIX) && b.contains(PREFIX)) return 1;
            return 0;
        }
    });

    for (String n : names) {
        System.out.println(n);
    }
}

OUTPUT (IMHO Han Solo must be prior to Hans because space is prior to s but you can easily modify my proposed conditions)

Han Solo
Hans
Kevin
Fritz
Peter

Upvotes: 4

Related Questions