问题
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