0

I tried to write this Java code that alphabetically sorts a string without using arrays, but it fails on the first word of the input string. I've tried to fix it but I'm unable to figure out a solution. I'm learning to code and i need a hint.

This Java code implements a function that sorts an input string alphabetically without using arrays. The function is called sortWords and it takes three arguments: phrase, which is the string to be sorted; splitBy, which is the character or string of characters that will be used to split the string into individual words; and order, which is a character that determines whether the output order should be ascending ('<') or descending ('>').

sortWords uses several helper functions to accomplish its task. The countWords function counts the number of words in a given string, using the given character or string of characters as a delimiter. The getWord function retrieves a specific word from a given string, using the given character or string of characters as a delimiter. And the replaceWord function replaces a specific word in a given string with another given word, using the given character or string of characters as a delimiter.

The main logic of sortWords is as follows:

  1. Count the number of words in the given string using countWords.
  2. Iterate over all the words in the string, from the first to the last.
  3. For each word, search for the smallest word that comes before it in the string and has not been processed yet.
  4. If a smaller word is found, replace it with the original word using replaceWord, and save the original word for later use.
  5. Repeat this process until all words in the string have been processed.
  6. If a descending order ('>') has been specified, reverse the order of the words in the string using an auxiliary string and getWord.
  7. Return the sorted string.

Expected result: 'arbol,bedel,casa,diploma,excursion,factura,guea,xilofono'. Since 'f' comes before 'g' in the alphabet.

Observed result: 'arbol,bedel,casa,diploma,excursion,guea,factura,xilofono'. I think it fails because factura is the first word in the input string.

    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args){
        /* sort alphabetically this string without using arrays*/
        String phrase;
        phrase = "factura,casa,arbol,xilofono,bedel,diploma,excursion,guea";
        System.out.println(sortWords(phrase, ",", '<'));
    }

    private static String getWord(String phrase, String splitBy, int number){
        String word;
        Scanner splitter;
        int count;
        splitter = new Scanner(phrase);
        splitter.useDelimiter(splitBy);
        word = "";
        count = 1;
        while (count <= number){
            word = splitter.next();
            count++;
        }
        return word;
    }

    private static int countWords(String phrase, String splitBy){
        int count;
        int position;
        count = 0;
        position = 0;
        while(position != -1){
            if (count == 0){
                position = phrase.indexOf(splitBy);
            } else {
                position = phrase.indexOf(splitBy, position + 1);
            }
            if (position != -1){
                count++;
            }
        }
        return count + 1;
    }

    private static String sortWords(String phrase, String splitBy, char order){
        int numberOfWords;
        numberOfWords = countWords(phrase, splitBy);
        for(int count = 1 ; count <= numberOfWords; count++){
            String wordToCheck;
            String wordToSwitchWith;
            int switchWithNumber;
            wordToCheck = getWord(phrase, splitBy, count);
            wordToSwitchWith = wordToCheck;
            switchWithNumber = count;
            for(int checkVS = count -1 ; checkVS >= 1; checkVS--){
                String wordToCompareWith;
                wordToCompareWith = getWord(phrase, splitBy, checkVS);
                if(wordToCheck.compareTo(wordToCompareWith) < 0 ){
                    wordToSwitchWith =  wordToCompareWith;
                    switchWithNumber = checkVS;
                }
            }
            phrase = replaceWord(phrase, splitBy, count, wordToSwitchWith);
            phrase = replaceWord(phrase, splitBy, switchWithNumber, wordToCheck);
        }
        if (order == '>'){
            String aux;
            aux = "";
            for(int count = numberOfWords ; count >= 1; count--){
                aux += getWord(phrase, splitBy, count);
                if(count != 1){
                    aux += splitBy;
                }
            }
            phrase = aux;
        }
        return phrase;
    }

    private static String replaceWord(String phrase, String splitBy, int position, String replacement){
        String output;
        int numberOfWords;
        output="";
        numberOfWords = countWords(phrase, splitBy);
        for(int count = 1 ; count <= numberOfWords; count++){
            if(count != position){
                output += getWord(phrase, splitBy, count);
            } else {
                output += replacement;
            }
            if(count != numberOfWords){
                output += splitBy;
            }
        }
        return output;
    }
}
manuelmsni
  • 23
  • 7
  • Your outer loop, `for(int count = 0 ; count <= numberOfWords; count++){`, runs one more time than the number of words (so 4 times if there are 3 words). Should it? The `number` parameter of your `getWord` method, is that 0-based or 1-based? Meaning, if there are three words, are they numbered 0, 1, 2 or is it 1, 2, 3? – Ole V.V. Dec 06 '22 at 04:43
  • 1
    It’s a good time to learn to use your debugger. – Ole V.V. Dec 06 '22 at 04:44
  • "Yeah, I had it as: "for (int count = 1; count <= numberOfWords; count++)", but it didn't work anyway. The number parameter is 1-based." – manuelmsni Dec 06 '22 at 05:08
  • The code seems to work for me. It gives: 'arbol,bedel,casa,diploma,excursion,guea,factura,xilofono'. What output were you expecting? – Joshua Spinak Dec 06 '22 at 05:13
  • Since 'f' comes before 'g' in the alphabet: 'arbol,bedel,casa,diploma,excursion,factura,guea,xilofono'. I think it fails because factura is the first word in the input string. – manuelmsni Dec 06 '22 at 05:16
  • Is your algorithm sound? Say you have input `con,bueno,a`. For `con` you are not comparing with any other word. For `bueno` you see that it should be switched with `con`, so you get `bueno,con,a`. Finally you look at `a`. It comes alphabetically before both of the other words, so you swap with the first of those, `bueno` and get `a,con,bueno`. Then your method returns. By the way your problem is not *only* with the first word in the input. I sorted `g,f,e,d,c,b,a` and got `a,g,f,e,d,c,b`. – Ole V.V. Dec 06 '22 at 05:55

2 Answers2

1

Here are a few tips for cleaner programming:

  1. Try to make the code easy enough to understand so that you don't need comments explaining what individual methods do. If you do need comments, include them.

  2. Break your code up into smaller methods. The following code could be included in a method called:
    private static String changeOrderIfAscending(String phrase, int numberOfWords, String splitBy, char order)

     if (order == '>'){
         String aux;
         aux = "";
         for(int count = numberOfWords ; count >= 1; count--){
             aux += getWord(phrase, splitBy, count);
             if(count != 1){
                 aux += splitBy;
             }
         }
         phrase = aux;
     }
    
  3. In the loop in the above method softwords you are using += for String concatenation. Instead, you could use StringBuilder:

     if (order == '>'){
         StringBuilder aux;
         aux = new StringBuilder();
         for(int count = numberOfWords ; count >= 1; count--){
             aux.append(getWord(phrase, splitBy, count));
             if(count != 1){
                 aux.append(splitBy);
             }
         }
         phrase = aux.toString();
     }
    

There are 2 other String concatenations with += in the method replaceWord.

Joshua Spinak
  • 133
  • 1
  • 1
  • 10
  • Thanks for the contribution. While what you say is considered good practice, I believe that it does not explain why the OP’s code is giving a wring result. – Ole V.V. Dec 06 '22 at 05:11
  • Oh, I didn't know about that. Does it improve efficiency, or is it just another way to do it? – manuelmsni Dec 06 '22 at 05:13
  • https://stackoverflow.com/questions/4645020/when-to-use-stringbuilder-in-java – Joshua Spinak Dec 06 '22 at 05:14
  • @OleV.V. I would have included this as a comment but it had too much text. – Joshua Spinak Dec 06 '22 at 05:17
  • Oh, thanks! This is the kind of thing my teacher doesn't even teach. So useful information. – manuelmsni Dec 06 '22 at 05:20
  • For a bit of a broader perspective the sorting algorithm used in inefficient, and keeping the items to be sorted in a string rather than a list or array is *very* inefficient, so not using a `StringBuilder` or `StringBuffer` is just one of the points and probably not the main one when it comes to efficiency. Still good to know, useful in many future programming tasks. – Ole V.V. Dec 06 '22 at 06:02
1

Here is an implementation of BubbleSort with your algorithms for sorting a string without using arrays.

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    /* sort alphabetically this string without using arrays*/
    String phrase;
    phrase = "g,f,e,d,c,b,a";
    phrase = "creo,que,sera,brasil,contra,francia,en,el,mundial"
    phrase = "factura,casa,arbol,xilofono,bedel,diploma,excursion,guea";
    System.out.println(bubbleSort(phrase, ",", '<'));
}

private static String bubbleSort(String phrase, String splitBy, char order) {
    int numberOfWords = countWords(phrase, splitBy);
    for (int i = 0; i < numberOfWords; i++)
        for (int j = 0; j < numberOfWords - i; j++)
            if (getWord(phrase, splitBy, j).compareTo(getWord(phrase, splitBy, j + 1)) > 0)
                phrase = swapWords(phrase, splitBy, j,j + 1);
    phrase = changeOrderIfAscending(phrase, numberOfWords, splitBy, order);
    return phrase;
}

private static int countWords(String phrase, String splitBy){
    int count;
    int position;
    count = 0;
    position = 0;
    while(position != -1){
        if (count == 0){
            position = phrase.indexOf(splitBy);
        } else {
            position = phrase.indexOf(splitBy, position + 1);
        }
        if (position != -1){
            count++;
        }
    }
    return count + 1;
}

private static String getWord(String phrase, String splitBy, int number){
    String word;
    Scanner splitter;
    int count;
    splitter = new Scanner(phrase);
    splitter.useDelimiter(splitBy);
    word = "";
    count = 1;
    while (count <= number){
        word = splitter.next();
        count++;
    }
    return word;
}

private static String swapWords(String phrase, String splitBy, int number1, int number2){
    String word1 = getWord(phrase, splitBy, number1);
    String word2 = getWord(phrase, splitBy, number2);
    phrase = replaceWord(phrase, splitBy, number1, word2);
    phrase = replaceWord(phrase, splitBy, number2, word1);
    return phrase;
}

private static String replaceWord(String phrase, String splitBy, int position, String replacement){
    String output;
    int numberOfWords;
    output="";
    numberOfWords = countWords(phrase, splitBy);
    for(int count = 1 ; count <= numberOfWords; count++){
        if(count != position){
            output += getWord(phrase, splitBy, count);
        } else {
            output += replacement;
        }
        if(count != numberOfWords){
            output += splitBy;
        }
    }
    return output;
}

private static String changeOrderIfAscending(String phrase, int numberOfWords, String splitBy, char order){
    if (order == '>'){
        StringBuilder aux;
        aux = new StringBuilder();
        for(int count = numberOfWords ; count >= 1; count--){
            aux.append(getWord(phrase, splitBy, count));
            if(count != 1){
                aux.append(splitBy);
            }
        }
        phrase = aux.toString();
    }
    return phrase;
}
Joshua Spinak
  • 133
  • 1
  • 1
  • 10