1

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

12
  • 1
    Don't create list of strings where each string represents single person. Java is Object Oriented language and most tools it provides use that fact. So create your own class like Person which will store name, surname, and will provide getters for those, then create list of Person objects and fill it with Person objects like people.add(new Person(n[0], n[1]));). Then creating Comparator for sorting method would be easier. For instance you could write something like people.sort(Comparator.comparing(Person::getSurname).thenComparing(Person::getName));. Commented Jan 4, 2021 at 16:03
  • You could also use a custom Comparator. See solution here: stackoverflow.com/a/6720298/10027577 Commented Jan 4, 2021 at 16:05
  • 1
    "nn[1]==lastname" use nn[1].equals(lastname) to compare strings. Also, unless you're using a version of Java prior to 8, list.sort() is simpler than Collections.sort(list). Commented Jan 4, 2021 at 16:05
  • Looks like you’re sorting samel but then not really doing anything with it and just using the original name List. You probably want to construct a new List via combining the results after sorting samel and that new list should be sorted as you’re describing. That said you could probably do it a little more concisely with a Comparator as others have said. Commented Jan 4, 2021 at 16:07
  • @KyleAure doesn't really help since I need a type player stats which it doesn't seem to have and when changing to the string I get an error. Commented Jan 4, 2021 at 16:11

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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

Comments

0

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?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.