Home > Blockchain >  Java: Most frequent numbers in a string
Java: Most frequent numbers in a string

Time:03-21

I am trying to write a program that receives some command line arguments, saves them as a string and finds the most frequent number in that string (it has to ignore letters and other symbols and just check the numbers). Then it should print the string, the most frequent number and how many times it comes up in that string. If two or more numbers come up in a string the same amount of times, it should write both of them in an ascending order.

I saw some solutions for similar programs using libraries and maps, but being a complete beginner I am still quite far from covering that, so I tried to solve the problem with the following code:

import java.util.*;

public class trying5 {
    public static void main(String[] args) {

        //string of arguments

        String s = "";
        if (args.length != 0) {
            for (int i = 0; i < args.length; i  ) {
                s = s   args[i]   " ";

            }
        }

        //I tried using an array

        /*char[] c = new char[s.length()];
        for (int i = 0; i < s.length(); i  ) {
            c[i] = s.charAt(i);
        }*/

        //Decided to use an arraylist

        ArrayList<Character> maxTimesNum = new ArrayList<Character>();
        //char[] maxTimesNum = new char[s.length()];

        int maxTimesNumRepeats = 0; //the  repeats of the most frequent number
        int index = 0;


        for (int i = 0; i < s.length(); i  ) {

            if (Character.isDigit(s.charAt(i))) {

                //variables for the possible number
                char x = s.charAt(i);
                int xRepeats = 0;

                for (int k = 0; k < s.length(); k  ) {
                    if (s.charAt(k) == x) {
                        xRepeats = xRepeats   1;
                    }
                }
                if (maxTimesNumRepeats < xRepeats) {
                    maxTimesNum.add(index, x);
                    maxTimesNumRepeats = xRepeats;

                } else if (maxTimesNumRepeats == xRepeats) {
                    if (!maxTimesNum.contains(x)){               
                    /*here I wanted it to check if we already have that number in the ArrayList so it doesn't print it multiple times*/
                            index = index   1;
                            maxTimesNum.add(index, x);
                    }
                }
            }
        }
        if (maxTimesNumRepeats > 0) {
            System.out.print("'"   s.trim()   "'"   " -> ");
            for (int p = 0; p < maxTimesNum.size(); p  ) {

                System.out.printf("%c ",maxTimesNum.get(p));

            }
            System.out.printf("(%d)\n", maxTimesNumRepeats);
        } else {
            System.out.println("The string "   "'"   s   "'"   " has no numbers.");
        }
    }
}

Unfortunately my code is not correct as it does not write the numbers in an ascending order, and I also can not seem to figure out why it works in some cases but not in the others. To describe the problem better, these are few of the inputs I tried:

Input: 386 40 253 987

Desired output: ' 386 40 253 987' -> 3 8 (2)

My output: ' 386 40 253 987' -> 3 8 (2)

Input: hdch44 fg1t525 j6s99

Desired output: 'hdch44 fg1t525 j6s99' -> 4 5 9 (2)

My output: 'hdch44 fg1t525 j6s99' -> 4 5 9 (2)

So these work. So did the inputs (Has no numbers) and (1234321). But it all goes downhill if the most frequent numbers do not occur in the string from the smallest to the largest:

Input: d8d 82 a1810y51

Desired output: 'd8d 82 a1810y51' -> 1 8 (3)

My output: 'd8d 82 a1810y51' -> 8 1 (3)

Or in this case, where I do not even understand why it wrote the number 4 as well as 2, as they do not show up the same amount of times:

Input: This(4) cat(3) and(3) friend(6), in(2) thought(7) seem(7) to(3), be(2), present(5) instead(5) they(4), roam(6) around(2) not(5) consciously(2) knowing(7), that(2) they(5) are(5) there(2) like(4), they(2) are(4), day(2) dreaming(5) together(2) today(5)!

Desired output: 'This(4) cat(3) and(3) friend(6), in(2) thought(7) seem(7) to(3), be(2), present(5) instead(5) they(4), roam(6) around(2) not(5) consciously(2) knowing(7), that(2) they(5) are(5) there(2) like(4), they(2) are(4), day(2) dreaming(5) together(2) today(5)!' -> 2 (9)

My output: 'This(4) cat(3) and(3) friend(6), in(2) thought(7) seem(7) to(3), be(2), present(5) instead(5) they(4), roam(6) around(2) not(5) consciously(2) knowing(7), that(2) they(5) are(5) there(2) like(4), they(2) are(4), day(2) dreaming(5) together(2) today(5)!' -> 2 4 (9)

I've updated my code so that I checked the repetitions of each number from a string and saved them to an ArrayList numRepeats. I proceed by trying to type out a result using multidimensional array. My new code:

import java.util.*;
public class trying7 {
    public static void main(String[] args) {
        //string of arguments
        String s = "";
        if (args.length != 0) {
            for (int i = 0; i < args.length; i  ) {
                s = s   args[i]   " ";

            }
        }

        //preštevanje ponovitev
        ArrayList<Integer> numRepeats = new ArrayList<Integer>(); // to save repeats of each number

        //int index = 0;
        int indexInt = 0;

        //Counting and saving repetitions of numbers
        for (int i = 0; i < s.length(); i  ) {

            if (Character.isDigit(s.charAt(i))) {
                char x = s.charAt(i);
                int xRepeats = 0;
                for (int k = 0; k < s.length(); k  ) {
                    if (s.charAt(k) == x) {
                        xRepeats = xRepeats   1;
                    }
                }
                numRepeats.add(indexInt, xRepeats);
                indexInt  ;
            }
        }

        //2d array
        int [][] tab = new int[2][numRepeats.size()];
        //saving number of repetitions under column with index 0 and rows j
        for (int j = 0; j < numRepeats.size(); j  ){
            tab [0][j] = numRepeats.get(j);
        }
        //saving numbers in a string under column with index 1
        for (int p = 0; p < s.length();p  ){
            tab [1][p]= s.charAt(p);
        }

        for (int d = 0; d < tab[0].length; d  ){
            int max = 0;
            for (int f= 0; f < tab.length; f  ){
                //here I am attempting to print the numbers with most repetitions 
            }
        }


        }//main
    }//class

CodePudding user response:

What is the problem?

From my understanding the algorithm is meant to take in a String and return the character (or characters if it is a tie) that appears the most. The issue in the code provided is how the characters are actually determined to be the most common character. Specifically in this (slightly reformatted) code:

    int maxTimesNumRepeats = 0; //the  repeats of the most frequent number
    int index = 0;

    for (int i = 0; i < s.length(); i  ) {
        if (Character.isDigit(s.charAt(i))) {
            //variables for the possible number
            char x = s.charAt(i);
            int xRepeats = 0;

            for (int k = 0; k < s.length(); k  ) {
                if (s.charAt(k) == x) {
                    xRepeats  ;
                }
            }
            if (maxTimesNumRepeats < xRepeats) {
                maxTimesNum.add(index, x);
                maxTimesNumRepeats = xRepeats;
            } 
            else if (maxTimesNumRepeats == xRepeats) {
                if (!maxTimesNum.contains(x)){               
                /*here I wanted it to check if we already have that number in the ArrayList so it doesn't print it multiple times*/
                    index = index   1;
                    maxTimesNum.add(index, x);
                }
            }
        }
    }

If we think about it from the program's perspective, the first time it runs through the outer for-loop, for (int i = 0; i < s.length(); i ), it does not know how many times the most frequent character appears. It just checks if the current character it is looking at is more than or equal to the maxTimesNumRepeats, which starts off at 0. This means the first character will always be counted (correctly or incorrectly) as a maxTimesNum, since it will appear more than 0 times! This explains why your output always includes the first digit that appears.

Now keep in mind that this problem will cause more issues than accidently counting the first digit. For example, if the current program gets given the input: "122333" it will incorrectly output the 1, the 2, and the 3. Because each number appeared more times than any number it had looked at so far.

Note: I use the terms character and number interchangeably due to the nature of this algorithm, hopefully this note clears up any confusion.

CodePudding user response:

Just another approach:

There are a couple of hurdles to overcome in order to carry out this particular task. The first of course would be to separate all the digits from the Command-Line arguments. This can be accomplished by using the String#replaceAll() method with a small Regular Expression (regex), for example:

Let's say we have a concatenated command-line string which contains: d8d 82 a1810y51:

String cmdLine = "d8d 82 a1810y51";
String numbers = cmdLine.replaceAll("\\D|\\s ", "");

System.out.println(numbers);

If run the following would be displayed within the console window: 882181051. What the regex does within the String#replaceAll() method is it tells the method to replace ALL non-digit characters (\\D) or any whitespaces (|\\s ) within the string it is working against with a null string ("") which essentially removes those characters. By doing so it only leaves a String of digits. If there were no digits within the command-line then obviously an empty string ("") would remain.

The second hurdle would be to find the number of occurrences for each digit contain within the numbers string. You can use an integer (int[]) array and a for loop for this, here is an example:

int[] occurred = new int[10];
    
/* Integer variable to hold the highest number of times a
   particular digit is found to be in the 'numbers' string. */
int high = 0;
    
/* Check for digit occurrences within the 'numbers' string. */
int idx;
for (int i = 0; i < numbers.length(); i  ) {
    idx = Integer.valueOf(numbers.substring(i, i 1));
    occurred[idx]  = 1;
    if (occurred[idx] > high) {
        high = occurred[idx];
    }
}

Now you almost have all the information required to build your output string, you just need to find which digits contain the same greatest occurrences and print it to the console window. Below is a runnable which demonstrates the concept discussed above. It really isn't much code. It's mostly comments which is intended for you to read and then delete as you see fit:

public class HighestCommandLineDigitDemo {

    
    public static void main(String[] args) {
        // A methodical approach to solving this task:
        
        /* Concantenate all Command-Line Arguments into a single String. */
        StringBuilder sb = new StringBuilder("");
        if (args.length != 0) {
            for (String arg : args) {
                if (!sb.toString().isEmpty()) {
                    sb.append(" ");
                }
                sb.append(arg);
            }
        }
        else {
            System.err.println("Application Error! No Command-Line Argument(s) To Process!");
            return;
        }

        // Print the args string that was created...
        String cmdLine = sb.toString();
        // Start the output string...
        String outputString = "'"   cmdLine   "' -> "; 
        
        /* Using the String#replaceAll() method and a small Regular
           Expression (regex), replace ALL non-numerical characters
           and whitespaces from the created args String.        */
        String numbers = cmdLine.replaceAll("\\D|\\s ", "");

        /* If it's found that there are no numbers within the args 
           string then we print "(0)" and exit the main() method
           which will effectively end the application.        */
        if (numbers.isEmpty()) {
            System.out.println("(0)");
            return;
        }

        /* Create an int[] array (occurred) and initialize it
           to hold 10 elements, 1 for each digit in the base 10 
           numerical system which is: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. 
           This array will hold the number of times each digit is 
           contained within the 'numbers' string.            */
        int[] occurred = new int[10];
        
        /* Integer variable to hold the highest number of times a
           particular digit is found to be in the 'numbers' string. */
        int high = 0;
        
        /* Check for digit occurrences within the 'numbers' string.
           Look at this loop very carefully! Can you see what it's
           doing and how it works? Read the comment for the next 
           `for` loop to get a hint.                          */
        int idx;
        for (int i = 0; i < numbers.length(); i  ) {
            idx = Integer.valueOf(numbers.substring(i, i 1));
            occurred[idx]  = 1;
            if (occurred[idx] > high) {
                high = occurred[idx];
            }
        }
        
        /* Reset the StringBuilder object we used earlier so that
           it's empty. We do this so we don't need to declare a 
           new StringBuilder object.                        */
        sb.setLength(0); 
        
        /* Now we take the information we have and build a string which 
           will contain the all the digits that were contained within 
           the created args string that there are the highest number of.
           All digits which there is the same highest of will be placed 
           into the string build. Keep in mind, since we're dealing with 
           a base 10 numerical system and the occurred[] array contains 
           a length of 10 because of that fact (elements in index range 
           of 0 to 9) the variable 'i' actually holds the digit which 
           relates the value contained within the occurred[] array at any 
           given iteration. This then means that each element within the
           occurred[] array is related to the digit which is the index 
           value to access that element. Any element within the array that 
           contains a value of 0 means that the index value to access that 
           element does not exist within the 'numbers' string and therefore 
           not in the Command-Line arguments. Read this 10 times if you have to :)                      */
        for (int i = 0; i < occurred.length; i  ) {
            if (occurred[i] == high) {
                sb.append(i).append(" ");
            }
        }
        sb.append("(").append(high).append(")");
        outputString  = sb.toString();
        System.out.println(outputString);  // Finish the output to User.
    }
}

Examples when run through code:

#1:
Concated Command-Line String:   12 34 5
Output:                         '12 34 5' -> 1 2 3 4 5 (1)

#2:
Concated Command-Line String:   hdch44 fg1t525 j6s99
Output:                         '12 34 5' -> 1 2 3 4 5 (1)

#3:
Concated Command-Line String:    386 40 253 987
Output:                         ' 386 40 253 987' -> 3 8 (2)
    
#4:
Concated Command-Line String:   Has no numbers
Output:                         'Has no numbers' -> (0)
    
#5:
Concated Command-Line String:   1234321
Output:                         '1234321' -> 1 2 3 (2)
    
#6:
Concated Command-Line String:   d8d 82 a1810y51
Output:                         'd8d 82 a1810y51' -> 1 8 (3)
    
#7:
Concated Command-Line String:   This(4) cat(3) and(3) friend(6), in(2) thought(7) seem(7) to(3), be(2), present(5) instead(5) they(4), roam(6) around(2) not(5) consciously(2) knowing(7), that(2) they(5) are(5) there(2) like(4), they(2) are(4), day(2) dreaming(5) together(2) today(5)!
Output:                         'This(4) cat(3) and(3) friend(6), in(2) thought(7) seem(7) to(3), be(2), present(5) instead(5) they(4), roam(6) around(2) not(5) consciously(2) knowing(7), that(2) they(5) are(5) there(2) like(4), they(2) are(4), day(2) dreaming(5) together(2) today(5)!' -> 2 (9)
  • Related