Be able to enter a value, and a string in a java scanner to output a ranking

I’m trying to create a ranking that displays this:

  1. int(value) – String(username)

(In total ten times even if I enter 30 values and 30 nicknames)

Here is my working code:

public class Methods {


    private static final ArrayList<Double> nbAll = new ArrayList<>();
    private static final ArrayList<String> pseudoAll = new ArrayList<>();

    public static void test() {
        try (Scanner scanner = new Scanner(System.in)) {

            System.out.print(ANSI_RED + "Please enter the number of notes you want to calculate : ");
            double nb = scanner.nextInt();
            String pseudo = scanner.next();

            for (int i = 0; i < nb; i++) {
                double temp = scanner.nextDouble();
                nbAll.add(temp);
            }


            System.out.println("------------");
            System.out.println("Ranking: ");
            nbAll.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
            retry();
        }
    }

I tried : To make a second for loop to be forced to enter the username in string but it didn’t work and for the ranking I didn’t succeed yet

Screen for Desired operation: https://i.imgur.com/0QlGHd8.png

Answer

In this particular case I personally think it may be a little better if you used a HashMap or Map Interface to store the required data. It’s rather ideal for this sort of thing since the User Name should be unique and can be used as the Key and the Rank as the Value since several Users could potentially contain the same rank value:

Map<String, Integer> map = new HashMap<>();

Another thing which may make life a little easier is for the User to enter the Rank AND the User Name related to that rank on a single line separated with a whitespace or a tab or whatever, for example:

Ranking #1:
Enter a Rank value followed by a User Name separated with space,
for example: 250 John Doe. Enter 'd' when done.
Your entry: --> |

Of course validation would need to be carried out so to ensure proper entry is done but this isn’t overly difficult using the String#matches() method and a small Regular Expression (regex), for example:

if (!myString.matches("^\d+\s+.{1,}$")) {
    System.err.println("Invalid Entry! Try again...");
    System.err.println();
    myString = "";
    continue;
}

What the regular expression "^\d+\s+.{1,}$" above passed to the String#matches() method does is that it validates the fact that the first component of the supplied User entry is in fact a string representation of a Integer value consisting of one or more digits. It then checks to make sure at least one whitespace follows that numerical value and then after the space it expects to see at least 1 (or more) of any characters after the space(s) which is to essentially be the User Name. Of course if the User enters the data incorrectly then an Invalid Entry warning would be issued and the user is given the opportunity to attempt the entry again.

Once valid input has been acquired the data now needs to of course be split into its’ respective data types before it can be applied to the the Map Interface object. This of course is done with the String#split() method:

String[] stringParts = myString.split("\s+");

This will create a String[] Array named stringParts. The \s+ regular expression tells the split() method to split the string on one or more whitespaces ' ' (or Tabs t, newlines n, Carriage Returns r, form-feeds f, and vertical tabulations x0B). This would cover pretty much all the cases for the Users required entry.

Now that we have the array we know that the first element of that array will be the supplied Ranking value. We want to convert this into an Integer data type before adding to our Map, like this:

int rank = Integer.parseInt(stringParts[0]);

Now we want the User Name. Because in this example we also allow for multiple names like First and Last names, a little more is involved to add the names together so to make a single User Name string from it all. Remember we split the data entry on whitespaces so if there are multiple names we could potentially have more than just two elements within the stringParts[] array. We’ll need to build the userName string. We use a for loop and the StringBuilder class to do this, for example:

String[] stringParts = tmp.split("\s+");
int rank = Integer.parseInt(stringParts [0]);
StringBuilder sb = new StringBuilder("");
for (int i = 1; i < stringParts .length; i++) {
    if (!sb.toString().isEmpty()) {
        sb.append(" ");
    }
    sb.append(stringParts [i]);
}
String userName = sb.toString();

Okay…now we have the User Name so let’s make sure a ranking with that User Name isn’t already contained within the Map:

if (map.containsKey(userName)) {
    System.err.println("A ranking for '" + userName 
                     + "' has already been supplied! Try again...");
    System.err.println();
    myString = "";
    continue;
}

If we pass to this point then all is good and we can add the data to the Map:

map.put(userName, rank);

This may seem a little long winded but in my opinion, it’s not. Below is a working example or all the above in use:

Scanner userInput = new Scanner(System.in);
Map<String, Integer> map = new HashMap<>();
    
int count = 0;
String tmp = "";
while (tmp.isEmpty()) {
    System.out.println("Ranking #" + (count+1) + ":");
    System.out.print("Enter a Rank value followed by a User Name separated "
            + "with space,nfor example: 250 John Doe. Enter 'd' when done.n"
            + "Your entry: --> ");
    tmp = userInput.nextLine();
    if (tmp.equalsIgnoreCase("d")) {
        break;
    }
    if (!tmp.matches("^\d+\s+.{1,}$")) {
        System.err.println("Invalid Entry! Try again...");
        System.err.println();
        tmp = "";
        continue;
    }
        
    String[] parts = tmp.split("\s+");
    int rank = Integer.parseInt(parts[0]);
    StringBuilder sb = new StringBuilder("");
    for (int i = 1; i < parts.length; i++) {
        if (!sb.toString().isEmpty()) {
            sb.append(" ");
        }
        sb.append(parts[i]);
    }
    String userName = sb.toString();
    if (map.containsKey(userName)) {
        System.err.println("A ranking for '" + userName 
                         + "' has already been supplied! Try again...");
        System.err.println();
        tmp = "";
        continue;
    }
    count++;
    map.put(userName, rank);
    tmp = "";
    System.out.println();
}

// Sort the map by RANK value in 'descending' order:
Map<String, Integer> sortedMap = map.entrySet().stream().sorted(Map.Entry.<String, 
        Integer>comparingByValue().reversed()).collect(java.util.stream.Collectors.toMap(Map.Entry::
        getKey, Map.Entry::getValue,(e1, e2) -> e1, java.util.LinkedHashMap::new));
    
// If you want the Rank values sorted in 'Ascending' order then use below instead:
/* Map<String, Integer> sortedMap2= map.entrySet().stream().sorted(Map.Entry.<String, 
        Integer>comparingByValue()).collect(java.util.stream.Collectors.toMap(Map.Entry::
        getKey, Map.Entry::getValue,(e1, e2) -> e1, java.util.LinkedHashMap::new));   */
    
// Display the rankings in Console Window:
System.out.println();
System.out.println("You entered " + count + " rankings and they are as follows:");
System.out.println();
    
// Table header
String header = String.format("%-4s %-15s %-6s", 
                              "No.", "User Name", "Rank");
System.out.println(header);
// The header underline
System.out.println(String.join("", java.util.Collections.nCopies(header.length(), "=")));
// The rankings in spaced format...
count = 1;
for (Map.Entry<String,Integer> enties : sortedMap.entrySet()) {
    System.out.printf("%-4s %-15s %-6d %n", 
                      String.valueOf(count) + ")", 
                      enties.getKey(), 
                      enties.getValue());
    count++;
}

When the above code is run, The User is asked to supply a Rank value and a User Name related to that rank. The User is then asked to enter another and another and another until that User enter ‘d’ (for done). All entries provided are then displayed within the Console Window in a table type format. The Rankings within the Map had been sorted in descending order before (highest rank first) before displaying them. If you prefer Ascending order then that code is also provided but is currently commented out.