Home > Software design >  Rearrange substrings using two criteria
Rearrange substrings using two criteria

Time:03-05

Given a String:

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

I need to sort its substrings first by numbers (in descending order) and then by letters, so that the result of the sort has the following form:

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

CodePudding user response:

To solve, the problem must be broken down into subproblems:

  1. Break down the input string into substrings
  2. Collect each substring into a list
  3. Create a comparator that compares the last (numeric) portion of the (sub)string and sorts them in descending order, and then by the beginning portion in ascending order
  4. Convert the list of substrings back into a string

Break down the input string into substrings

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)

Collect each substring into a list

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

The code above will iterate through all of the groups that matched the regex pattern and adds them to a list.

Create a comparator

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

The comparator I created compares the last portion of the substrings (after the dash) and sorts them in descending order (reversed()). Then, the intermediate result is sorted in ascending order from the beginning of the substring up to the dash. Basically it sorts the intermediate result in alphabetical order since the substrings start with a letter.

Convert the list of substrings back into a string

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

I created a test program to run this:

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

Which results in the following:

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

CodePudding user response:

One alternative for your specific example is to use stream:

public static String sort(String str) {

    String[] arr = Arrays.stream(str.replaceAll(" - ", " ")
        .split(" ")).filter(Predicate.not("-"::equals))
        .toArray(String[]::new);

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

Then:

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

String strSorted = sort(str);

System.out.println(strSorted);

Output:

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

CodePudding user response:

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( " " ) ) );
  • Related