使用两个条件重新排列子字符串

Rearrange substrings using two criteria

给定一个字符串:

a - 11 h - 19 l - 18 d - 19

我需要先按数字(降序)对它的子串进行排序,然后再按字母排序,这样排序的结果就是下面的形式:

d - 19 h - 19 l - 18 a - 11

要解决,问题必须分解成子问题:

  1. 将输入字符串分解为子字符串
  2. 将每个子字符串收集到一个列表中
  3. 创建一个比较器,比较(子)字符串的最后(数字)部分并按降序对它们进行排序,然后按开始部分按升序排序
  4. 将子字符串列表转换回字符串

将输入字符串分解为子字符串

String regex = "\w\s-\s\d+";
String input = "a - 11 h - 19 l - 18 d - 19";

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
matcher.toMatchResult().groupCount(); // this should return 4 (won't be used in final code)

将每个子字符串收集到一个列表中

List<String> strings = new ArrayList<>();
while (matcher.find()) {
    strings.add(matcher.group());
}

上面的代码将遍历所有匹配正则表达式模式的组,并将它们添加到列表中。

创建比较器

Comparator<String> compareBySubstring = Comparator.comparing((String s) -> s.substring(s.indexOf(" -")))
        .reversed().thenComparing((String s) -> s.substring(0, s.indexOf("-")));
List<String> newList = strings.stream().sorted(compareBySubstring).collect(Collectors.toList());

我创建的比较器比较子字符串的最后部分(破折号之后)并按降序对它们进行排序 (reversed())。然后,中间结果从子字符串的开头到破折号按升序排序。基本上它按字母顺序对中间结果进行排序,因为子字符串以字母开头。

将子字符串列表转换回字符串

        StringBuilder buffer = new StringBuilder();
        newList.forEach(item -> {
            buffer.append(item + " "); // done to reinsert the space that separated each substring.
        });

我为 运行 创建了一个测试程序:

public class SortBySubstringDemo {
    public static void main(String[] args) {
        String regex = "\w\s-\s\d+";
        String input = "a - 11 h - 19 l - 18 d - 19";
        System.out.println("Input:\n" + input);

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);
        List<String> strings = new ArrayList<>();
        while (matcher.find()) {
            strings.add(matcher.group());
        }

        Comparator<String> compareBySubstring = Comparator.comparing((String s) -> s.substring(s.indexOf(" -")))
                .reversed().thenComparing((String s) -> s.substring(0, s.indexOf("-")));
        List<String> newList = strings.stream().sorted(compareBySubstring).collect(Collectors.toList());

        StringBuilder buffer = new StringBuilder();
        newList.forEach(item -> {
            buffer.append(item + " ");
        });
        String output = buffer.toString().trim();
        System.out.println("Output:\n" + output);
    }
}

结果如下:

Input:
a - 11 h - 19 l - 18 d - 19
Output:
d - 19 h - 19 l - 18 a - 11

您的具体示例的另一种选择是使用 stream:

  1. " - " 替换为 " " 以获得由字母和数字以空格分隔的字符串。
  2. 使用 " " 拆分得到一个 array,其中字母在偶数索引中,数字在奇数索引中。
  3. 使用偶数索引创建一个 IntStream 并将它们映射到 Map.Entry<String,Integer> 使用 array[index] 作为键和 array[index + 1] 作为值。
  4. 使用先比较值然后比较键的比较器对 stream 进行排序。
  5. 将条目 stream 映射到具有 key + " - " + value 的字符串。
  6. 最后,使用 Collectors.joining(" ")stream 收集到一个字符串中,用空格分隔每个 "letter - number"

总结在一个方法中:

public static String sort(String str) {

    String[] arr = str.replaceAll(" - ", " ").split(" ");

    Comparator<Map.Entry<String, Integer>> comparator = Comparator
        .comparingInt(Map.Entry<String, Integer>::getValue).reversed()
        .thenComparing(Map.Entry::getKey);
    
    return IntStream.range(0, arr.length).filter(i -> i % 2 == 0)
        .mapToObj(i -> Map.entry(arr[i], Integer.parseInt(arr[i + 1])))
        .sorted(comparator).map(entry -> entry.getKey() + " - " + entry.getValue())
        .collect(Collectors.joining(" "));
}

测试:

String str = "a - 11 h - 19 l - 18 d - 19";

String strSorted = sort(str);

System.out.println(strSorted);

输出:

d - 19 h - 19 l - 18 a - 11
String s = "a - 11 h - 19 l - 18 d - 19";

record Pair(String string, int number) { }
System.out.println( new Scanner( s ).findAll( "(\w)\s-\s(\d+)" )
                        .map( matchResult -> new Pair( matchResult.group( 1 ), Integer.parseInt( matchResult.group( 2 ) ) ) )
                        .sorted( Comparator.<Pair>comparingInt( Pair::number ).reversed().thenComparing( Pair::string ) )
                        .map( pair -> pair.string() + " - " + pair.number() )
                        .collect( Collectors.joining( " " ) ) );