Как использовать вторичную алфавитную сортировку в строковом списке имен?

Я пытаюсь сделать так, чтобы при задании строки имен, которые являются именами и фамилиями, где имена разделены на ;, а имя разделено на фамилию на : ->

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

Я хочу вернуть строку, в которой все имена отсортированы в верхнем регистре и отсортированы в алфавитном порядке в соответствии с фамилией и теми, которые имеют одну и ту же фамилию, а затем снова отсортированы (вторичная сортировка) между именами людей, которые имеют одинаковые Фамилия. (Я также специально меняю все на ПРОПИСНЫЕ).

И вернуть строку отсортированных имен, поэтому для приведенного выше примера она должна возвращаться:

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

который сортирует по фамилии в алфавитном порядке, затем сортирует между теми, у кого одна и та же фамилия, вторичную сортировку по именам.

Мне удалось выполнить основную сортировку, то есть сортировку фамилий по алфавиту, но я не знаю, как выполнить вторичную сортировку (между теми, у кого одна и та же фамилия). Я думал разбить на подмассивы или что-то подобное, но я не уверен, как это сделать.

Каково решение для этого?

Вот что мне удалось до сих пор (работает на основной сортировке):

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;
    }
}

Я попытался сделать вторичную сортировку и не увенчался успехом.

Если я не был достаточно ясен, есть две сортировки: основная по фамилиям в алфавитном порядке и вторичная сортировка, которая происходит только для людей с одинаковой фамилией, тогда их имена сортируются соответственно по алфавиту. Итак, если у нас есть два человека по имени Мэтт Кон и Милл Кон, то у них одна и та же фамилия и одна и та же первая буква в фамилии, но а стоит перед о, поэтому вывод должен быть (COHN , MATT)(COHN, MILL), а не наоборот.

В решении внизу я запустил его и получил ошибку:

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

но оказалось ложным


person Rocky cohn    schedule 04.01.2021    source источник
comment
Не создавайте список строк, где каждая строка представляет одного человека. Java — это объектно-ориентированный язык, и большинство предоставляемых им инструментов используют этот факт. Итак, создайте свой собственный класс, такой как Person, который будет хранить имя, фамилию и предоставит для них геттеры, затем создайте список объектов Person и заполните его объектами Person, такими как people.add(new Person(n[0], n[1]));). Тогда создание Comparator для метода сортировки будет проще. Например, вы можете написать что-то вроде people.sort(Comparator.comparing(Person::getSurname).thenComparing(Person::getName));.   -  person Pshemo    schedule 04.01.2021
comment
Вы также можете использовать собственный компаратор. См. решение здесь: stackoverflow.com/a/6720298/10027577   -  person KyleAure    schedule 04.01.2021
comment
nn[1]==lastname используйте nn[1].equals(lastname) для сравнения строк. Кроме того, если вы не используете версию Java до 8, list.sort() проще, чем Collections.sort(list).   -  person Andy Turner    schedule 04.01.2021
comment
Похоже, вы сортируете тот же список, но ничего с ним не делаете и просто используете исходное имя List. Вы, вероятно, захотите создать новый список, объединив результаты после сортировки того же самого, и этот новый список должен быть отсортирован, как вы описываете. Тем не менее, вы, вероятно, могли бы сделать это немного более лаконично с компаратором, как говорили другие.   -  person Slayer0248    schedule 04.01.2021
comment
@KyleAure на самом деле не помогает, так как мне нужна статистика типа игрока, которой, похоже, нет, и при переходе на строку я получаю сообщение об ошибке.   -  person Rocky cohn    schedule 04.01.2021
comment
Я думаю, что во вводе опечатка или в выводе Alfred:Corwil должно быть Alfred:Corwill с двойным ll в конце?   -  person YCF_L    schedule 04.01.2021
comment
исправил, мой косяк. @YCF_L проверьте, что я добавил, пожалуйста. Спасибо   -  person Rocky cohn    schedule 04.01.2021
comment
Почему вы хотите получить MADISON после VICTORIA?   -  person YCF_L    schedule 04.01.2021
comment
это просто пример, чтобы проверить, работает ли код, ожидаемое - это то, что должно быть правильным, а фактическое - это то, что я получил по вашему коду. сортировка идет сначала по фамилии, а затем только по тем, у кого одна и та же фамилия, которые сортируются по имени, а c предшествует t в алфавитном порядке.   -  person Rocky cohn    schedule 04.01.2021
comment
-Почему ты хочешь получить MADISON после VICTORIA? неуместно использовать вторичную сортировку, поскольку у них разные фамилии-> SCHWARZ!=STAN   -  person Rocky cohn    schedule 04.01.2021
comment
Я не могу найти решение прямо сейчас, но если кто-то получил его, не стесняйтесь, пингуйте меня, пожалуйста, мое фактическое решение 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());   -  person YCF_L    schedule 04.01.2021
comment
Давайте продолжим это обсуждение в чате.   -  person YCF_L    schedule 05.01.2021


Ответы (3)


Одно из возможных решений может быть таким:

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);
        }
    }
}

Решение без потоков:

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);
        }
    }
}

И тест:

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);
    }
}

Бонусный раунд с использованием Java 15 с функцией предварительного просмотра:

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);
        }
    }
}
person Mr.Robot    schedule 04.01.2021

Если вы используете поток Java, вы можете использовать sorted следующим образом:

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());

Результаты

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

Я бы также предложил использовать объекты, а не одну строку для хранения вашей информации, это не очень хорошая практика.

person YCF_L    schedule 04.01.2021

Вы можете использовать цепочку компараторов для сортировки сначала по одному значению, а затем по другому значению:

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);

Выход:

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

См. также: Как сортировать по полю класса с собственным компаратором?

person Community    schedule 05.01.2021