Private Sorting Rule in a Stream Java
Asked Answered
R

3

5

Hey if anyone has an idea I would be really thankfull. I'm in a Java stream and i would like to sort my list that i'll be returning. I need to sort the list via TradPrefis ( MyObject::getTradPrefix ). But this would be way too easy. Because i want to sort following the number at the end of TradPrefix exampleTradPrefix_[NUMBER TO SORT]

Exemple : hello_1 test_2 ... still_there_22

Here is a piece of code so you can imagine easier.

public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
    LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
    return (toReturn.stream()
            .map(this::createWsQuestion)
            .sorted(comparing(WsQuestion::getTradPrefix.toString().length()))
            .collect(Collectors.toCollection(LinkedHashSet::new)));
}
Rugging answered 12/11, 2018 at 16:4 Comment(0)
I
3

If I where you I would simply put a method on the WsQuestion class, let's call it sort order:

public int getSortOrder() {
  return Integer.valueOf(tradPrefix.substring(tradPrefix.lastIndexOf("_") + 1));
}

The Integer parse is needed since comparing strings would give "11" < "2" (thanks Holger for pointing this out). The lastIndexOf() makes sure that any number of underscores are allowed in tradPrefix, as long as there is at least one.

Then simply create a comparotor by using Comparator.comparingInt()

public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
  LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
  return (toReturn.stream()
      .map(this::createWsQuestion)
      .sorted(comparingInt(WsQuestion::getSortOrder))
      .collect(Collectors.toCollection(LinkedHashSet::new)));
}
Ial answered 12/11, 2018 at 19:28 Comment(2)
Since the numbers in the example are in the range 1 to 22, parsing the string is needed, as when comparing strings, "11" < "2". You can only skip parsing to int when you can preclude differently sized strings and leading zeros or when you add special handling for these cases.Smithsonite
@Smithsonite Of course, forgot to think about that scenario. I updated the answer. Thanks!Ial
C
4

One method would simply be to split getTradPrefix().toString() by _ and parse the rightmost value as an int, and use it to sort the Stream:

public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
    LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
    return toReturn.stream()
        .map(this::createWsQuestion)
        .sorted(Comparator.comparingInt(question -> {
            String[] args = question.getTradPrefix().toString().split("_");
            return Integer.parseInt(args[args.length - 1]);
        }))
        .collect(Collectors.toCollection(LinkedHashSet::new));
}
Clemenciaclemency answered 12/11, 2018 at 16:7 Comment(3)
What about "still_there_22"?Brandenbrandenburg
i guess that i'll just have to change the return Integer.parseInt(args[1]); to return Integer.parseInt(args[args.length - 1]);Rugging
Don’t use .split("_") to populate an array when you are only interested in the last element. You can use .replaceFirst(".*_", ""), for example, to only get the part after the last _. With that, you could even make it a one liner again.Smithsonite
I
3

If I where you I would simply put a method on the WsQuestion class, let's call it sort order:

public int getSortOrder() {
  return Integer.valueOf(tradPrefix.substring(tradPrefix.lastIndexOf("_") + 1));
}

The Integer parse is needed since comparing strings would give "11" < "2" (thanks Holger for pointing this out). The lastIndexOf() makes sure that any number of underscores are allowed in tradPrefix, as long as there is at least one.

Then simply create a comparotor by using Comparator.comparingInt()

public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
  LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
  return (toReturn.stream()
      .map(this::createWsQuestion)
      .sorted(comparingInt(WsQuestion::getSortOrder))
      .collect(Collectors.toCollection(LinkedHashSet::new)));
}
Ial answered 12/11, 2018 at 19:28 Comment(2)
Since the numbers in the example are in the range 1 to 22, parsing the string is needed, as when comparing strings, "11" < "2". You can only skip parsing to int when you can preclude differently sized strings and leading zeros or when you add special handling for these cases.Smithsonite
@Smithsonite Of course, forgot to think about that scenario. I updated the answer. Thanks!Ial
G
2

You can make a small Comparator like this:

  private static final Comparator<String> questionComparator = Comparator.comparingInt(s -> {
    String[] pieces = s.split("_");
    return Integer.parseInt(pieces[pieces.length-1]);
  });

Then use it in your sorted().

Having a separate Comparator will make your code more readable too, since you will be separating concerns.

return toReturn.stream()
            .map(this::createWsQuestion)
            .sorted(questionComparator)
            .collect(Collectors.toCollection(LinkedHashSet::new));
Germ answered 12/11, 2018 at 16:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.