How to use a secondary alphabetical sort on a string list of names?

♀尐吖头ヾ 提交于 2021-02-16 20:59:53

问题


I'm trying a way that when given a string of names which are first and last names, where names are split by ; and first name split to last name by : ->

"Fred:Corwill;Wilfred:Corwill;Barney:Tornbull;Betty:Tornbull;Bjon:Tornbull;Raphael:Corwill;Alfred:Corwill"

I want to return a string which is sorted out all the names as uppercase and sorted alphabetically according to the last name and the ones that share the same last name then sort again (secondary sort) between the first names of the people that share the same last name. (I also on purpose change everything to UPPERCASE).

And return a string of the sorted names, so for the example above it should return:

"(CORWILL, ALFRED)(CORWILL, FRED)(CORWILL, RAPHAEL)(CORWILL, WILFRED)(TORNBULL, BARNEY)(TORNBULL, BETTY)(TORNBULL, BJON)"

which sorts according to the last name alphabetically then sorts between those who share the same last name a secondary sort of the first names.

I managed to do the main sorting which is sorting the last names alphabetically but I'm not sure how to do the secondary sorting (between those who share the same last name). I was thinking to split into sub-arrays or something similar but I'm not sure how to do this.

What is the solution for this?

Here is what I managed so far (works on main sorting):

public class Solution {
    public static String meeting(String s) {
        String m = "";
        List<String> name = new ArrayList<String>();
        for (String i : s.toUpperCase().split(";")) {
            String[] n = i.split(":");
            name.add(n[0] + " " + n[1]);
        }
        //main sorting
        java.util.Collections.sort(name);
        //secondary sorting and go through each different last name
        for (String i : name) {
            String[] n = i.split(" ");
            String lastname = n[1];
            // new list for the ppl who share same last name
            List<String> samel = new ArrayList<String>();
            samel.add(n[0]);
            for (String ss : name) {
                String[] nn = ss.split(" ");
                if (nn[1] == lastname) {
                    samel.add(nn[0]);
                }
            }
            //sort the ppl alphabetically with the same last name
            java.util.Collections.sort(samel);
        }
        for (String i : name) {
            String[] n = i.split(" ");
            m += ("(" + n[0] + " , " + n[1] + ")");
        }
        return m;
    }
}

I attempted to do the secondary sorting and was not successful.

If I wasn't clear enough, there are two sortings, the main which is by last names alphabetically and the secondary sorting which only happens on people who share the exact same last name then their first names get sorted accordingly by alphabet. So, if we have two persons called matt cohn and mill cohn, then they share the same last name and the same first letter in the last name, but a is before o, so the output should be (COHN , MATT)(COHN, MILL) and not the other way round.

On the solution down below I ran it and got an error:

input String:
Alexis:Wahl;John:Bell;Victoria:Schwarz;Abba:Dorny;Grace:Meta;Ann:Arno;Madison:STAN;Alex:Cornwell;Lewis:Kern;Megan:Stan;Alex:Korn

Actual: (ARNO, ANN)(BELL, JOHN)(CORNWELL, ALEX)(DORNY, ABBA)(KERN, LEWIS)(KORN, ALEX)(META, GRACE)(STAN, MADISON)(SCHWARZ, VICTORIA)(STAN, MEGAN)(WAHL, ALEXIS)

Expect: (ARNO, ANN)(BELL, JOHN)(CORNWELL, ALEX)(DORNY, ABBA)(KERN, LEWIS)(KORN, ALEX)(META, GRACE)(SCHWARZ, VICTORIA)(STAN, MADISON)(STAN, MEGAN)(WAHL, ALEXIS)
false

but came out as false


回答1:


One possible solution could be like so:

public class ExtractAndSortNames {

    public static String extractAndSort(String s) {
        return Stream.of(s.split(";"))
                .map(String::toUpperCase)
                .map(ExtractAndSortNames::extractPersonFirstNameAndLastName)
                .sorted(Comparator.comparing(FirstNameAndLastName::getLastName).thenComparing(FirstNameAndLastName::getFirstName))
                .map(FirstNameAndLastName::format)
                .collect(Collectors.joining());
    }

    private static FirstNameAndLastName extractPersonFirstNameAndLastName(String personName) {
        var split = personName.split(":");
        var firstName = split[0];
        var lastName = split[1];
        return new FirstNameAndLastName(firstName, lastName);
    }

    private static class FirstNameAndLastName {
        private final String firstName;
        private final String lastName;

        public FirstNameAndLastName(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
        
        public String getFirstName() {
            return firstName;
        }
        
        public String getLastName() {
            return lastName;
        }
        
        public String format() {
            return String.format("(%s, %s)", this.lastName, this.firstName);
        }
    }
}

Solution without streams:

public class ExtractAndSortNames {

    public static String extractAndSort(String s) {
        List<FirstNameAndLastName> toSort = new ArrayList<>();
        for (String s1 : s.split(";")) {
            String toUpperCase = s1.toUpperCase();
            FirstNameAndLastName firstNameAndLastName = extractPersonFirstNameAndLastName(toUpperCase);
            toSort.add(firstNameAndLastName);
        }
        toSort.sort(Comparator.comparing(FirstNameAndLastName::getLastName).thenComparing(FirstNameAndLastName::getFirstName));
        StringBuilder sb = new StringBuilder();
        for (FirstNameAndLastName firstNameAndLastName : toSort) {
            String format = firstNameAndLastName.format();
            sb.append(format);
        }
        return sb.toString();
    }

    private static FirstNameAndLastName extractPersonFirstNameAndLastName(String personName) {
        var split = personName.split(":");
        var firstName = split[0];
        var lastName = split[1];
        return new FirstNameAndLastName(firstName, lastName);
    }

    private static class FirstNameAndLastName {
        private final String firstName;
        private final String lastName;

        public FirstNameAndLastName(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public String getFirstName() {
            return firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public String format() {
            return String.format("(%s, %s)", this.lastName, this.firstName);
        }
    }
}

And test:

class ExtractAndSortNamesTest {

    @Test
    void test() {
        var input = "Fred:Corwill;Wilfred:Corwill;Barney:Tornbull;Betty:Tornbull;Bjon:Tornbull;Raphael:Corwill;Alfred:Corwill";
        var result = ExtractAndSortNames.extractAndSort(input);

        assertEquals("(CORWILL, ALFRED)(CORWILL, FRED)(CORWILL, RAPHAEL)(CORWILL, WILFRED)(TORNBULL, BARNEY)(TORNBULL, BETTY)(TORNBULL, BJON)", result);
    }

    @Test
    void test2() {
        var input = "Alexis:Wahl;John:Bell;Victoria:Schwarz;Abba:Dorny;Grace:Meta;Ann:Arno;Madison:STAN;Alex:Cornwell;Lewis:Kern;Megan:Stan;Alex:Korn";
        var result = ExtractAndSortNames.extractAndSort(input);

        assertEquals("(ARNO, ANN)(BELL, JOHN)(CORNWELL, ALEX)(DORNY, ABBA)(KERN, LEWIS)(KORN, ALEX)(META, GRACE)(SCHWARZ, VICTORIA)(STAN, MADISON)(STAN, MEGAN)(WAHL, ALEXIS)", result);
    }
}

Bonus round using Java 15 with preview feature:

public class ExtractAndSortNames {

    public static String extractAndSort(String s) {
        return Stream.of(s.split(";"))
                .map(String::toUpperCase)
                .map(FirstNameAndLastName::from)
                .sorted(Comparator.comparing(FirstNameAndLastName::lastName).thenComparing(FirstNameAndLastName::firstName))
                .map(FirstNameAndLastName::format)
                .collect(Collectors.joining());
    }

    private static record FirstNameAndLastName (String firstName, String lastName) {
        private static FirstNameAndLastName from(String personName) {
            var split = personName.split(":");
            var firstName = split[0];
            var lastName = split[1];

            return new FirstNameAndLastName(firstName, lastName);
        }

        public String format() {
            return "(%s, %s)".formatted(this.lastName, this.firstName);
        }
    }
}



回答2:


If you are using Java stream you can use sorted like this:

return Arrays.stream(s.split(";"))
        .map(p -> p.split(":"))
        .sorted(Comparator.comparing((String[] p) -> p[1]).thenComparing((String[] p) -> p[0]))
        .map(p -> String.format("(%s, %s)", p[1].toUpperCase(), p[0].toUpperCase()))
        .collect(Collectors.joining());

Outputs

(CORWILL, ALFRED)(CORWILL, FRED)(CORWILL, RAPHAEL)(CORWILL, WILFRED)(TORNBULL, BARNEY)(TORNBULL, BETTY)(TORNBULL, BJON)

I would also suggest to use Objects and not one String to store your information, it is not a good practice to use this.




回答3:


You can use a chain of comparators to sort first by one value and then by another value:

String str = "Fred:Corwill;Wilfred:Corwill;Barney:Tornbull;" +
        "Betty:Tornbull;Bjon:Tornbull;Raphael:Corwill;Alfred:Corwill";

List<Map.Entry<String, String>> list = Stream.of(str)
        .map(String::toUpperCase)
        // Stream<String>
        .flatMap(s -> Arrays.stream(s.split(";")))
        // Stream<String[]>
        .map(s -> s.split(":"))
        // Stream<Map.Entry<String,String>>
        .map(arr -> Map.entry(arr[1], arr[0]))
        // sort by last name, then by first name
        .sorted(Map.Entry.<String, String>comparingByKey()
                .thenComparing(Map.Entry::getValue))
        .collect(Collectors.toList());

// output line by line
list.forEach(System.out::println);

Output:

CORWILL=ALFRED
CORWILL=FRED
CORWILL=RAPHAEL
CORWILL=WILFRED
TORNBULL=BARNEY
TORNBULL=BETTY
TORNBULL=BJON

See also: How to sort by a field of class with its own comparator?



来源:https://stackoverflow.com/questions/65565641/how-to-use-a-secondary-alphabetical-sort-on-a-string-list-of-names

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!