5

I have to create a program that counts the letters in string and I have a little problem with that.

This is my code in main:

Scanner sc = new Scanner(System.in);
String str;
int count;

System.out.println("Enter some text: ");
str = sc.nextLine();

char ch;

System.out.println("Letters: ");
for (ch = (char) 65; ch <= 90; ch++) {
    count = 0;
    for (int i = 0; i < str.length(); i++) {
        if (ch == str.charAt(i) || (ch + 32) == str.charAt(i)) {
            count++;
        }
    }
    if (count > 0) {
        System.out.println(ch + ": " + count);
    }
}

Everything looks fine, but the output should not be in alphabetical order, rather ordered by the number of letters descending.

For example, if you input Hello World, the output should be something like this:

L: 3
O: 2
H: 1
D: 1
E: 1
R: 1
W: 1

The output would be sorted in descending order of letter frequency. That means the most frequent letter should appear first and the least last.
The order for letters that appears in equal proportions must be in alphabetical order.

Sisi
  • 111
  • 2
  • 12
  • When I run your program, I get the expected result. And the code looks good as well. – Nikem May 22 '16 at 15:13
  • What is your expected output? – Java Geo May 22 '16 at 15:14
  • Something like this: L: 3 O: 2 H: 1 D: 1 E: 1 R: 1 W: 1 In nonalphabetical order – Sisi May 22 '16 at 15:16
  • Why such output is expected? You count letters in alphabetical order, why do you expect output not to be? – Nikem May 22 '16 at 15:20
  • I think u told actual output.. I mean what should be the output – Java Geo May 22 '16 at 15:20
  • It looks like he wants them to be sorted in descending order of letter frequency, but I don't get what's the order for letters of equal frequency. I would have guessed order of apparition, but then the E would be before the D – Aaron May 22 '16 at 15:21
  • D: 1 E: 1 H: 1 L: 3 O: 4 R: 1 W: 1 This is the output I get when I run – Java Geo May 22 '16 at 15:21
  • This is the output: D: 1 E: 1 H: 1 L: 3 O: 2 R: 1 W: 1 It's in alphabetical order. I won't to be like this. I want to be like i wrote in the post :) – Sisi May 22 '16 at 15:23
  • You should have worded the question that way! – Alvin Bunk May 22 '16 at 15:24
  • @Sisi except it's hard to get what you want from a single example. Sure that's the output for "hello world", but what should the output for "goodbye world" be? Or "Hello, World!" ? It's best if you can put the reasoning in words in addition to one or more examples – Aaron May 22 '16 at 15:26
  • Why you want the output so? Yes put more inputs and outputs... Expected and Actual – Java Geo May 22 '16 at 15:26
  • @Sisi to help you edit your post : can you confirm you want the output to be sorted in descending order of letter frequency? That means the most frequent letter should appear first, and the least last. Also, what should the order be for letters that appears in equal proportions? Why must the D appear before the E ? – Aaron May 22 '16 at 15:29
  • @Aaron Yes, that's what i want. – Sisi May 22 '16 at 15:34
  • And what about letters with the same frequency? Maybe you just don't care about their order? – Aaron May 22 '16 at 15:35
  • @Sisi : Posted an approach. try whether it's feasible – Java Geo May 22 '16 at 15:56

3 Answers3

3

The problem is that your outer loop browse the letters in alphabetical order, and that's where you display the count.

I would instead recommend browsing the input string with a single loop, updating the count of each letter in a Map<Character, Integer> as I encounter them.
Then once the input String has been consumed, I would sort the Map by descending values, and print each key/value pair.

Map<Character, Integer> lettersCount = new HashMap<>();

for (int i=0; i <str.length(); i++) {
    Character current = str.charAt(i);
    if (Character.isLetter(current)) {
        Integer previousCount = lettersCount.get(current);
        if (previousCount != null) {
            lettersCount.put(current, previousCount + 1);
        } else {
            lettersCount.put(current, 1);
        }
    }
}
List<Map.Entry<Character, Integer>> list = new LinkedList<Map.Entry<Character, Integer>>( lettersCount.entrySet() );
Collections.sort( list, new Comparator<Map.Entry<Character, Integer>>()
{
    public int compare( Map.Entry<Character, Integer> o1, Map.Entry<Character, Integer> o2 )
    {
        return (o2.getValue()).compareTo( o1.getValue() );
    }
} );
for (Map.Entry<Character, Integer> entry : list) {
    System.out.println(entry.getKey() + " : " + entry.getValue());
}

You can try it out on ideone.

As you can see, sorting a Map by values isn't trivial :-/

Aaron
  • 24,009
  • 2
  • 33
  • 57
1

If you want to sort the results then you'll have to store the results & then iterate over them by their count descending to print in order

The best data structure to store them into would be a heap, keyed off of count. Java supplies such a data structure as java.util.PriorityQueue which can take a comparator function which first compares count & then character

https://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html

Demur Rumed
  • 421
  • 6
  • 17
1
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

public class Main {

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

        System.out.println("Enter some text: ");
        str = sc.nextLine();

        char ch;

        System.out.println("Letters: ");
        LinkedHashMap<String, Integer> charCountMap = new LinkedHashMap<String, Integer>();
        for (ch = (char) 65; ch <= 90; ch++) {
            count = 0;
            for (int i = 0; i < str.length(); i++) {
                if (ch == str.charAt(i) || (ch + 32) == str.charAt(i)) {
                    count++;
                }
            }
            if (count > 0) {
                System.out.println(ch + ": " + count);
                charCountMap.put(ch + "", count);
            }

        }
        LinkedHashMap<String, Integer> sortedMapBasedOnValues = sortHashMapByValues(charCountMap);

        for (Map.Entry<String, Integer> entry : sortedMapBasedOnValues.entrySet()) {
            System.out.println("Key : " + entry.getKey() + " Value : " + entry.getValue());
        }

    }
    // Following method used from
    // http://stackoverflow.com/questions/8119366/sorting-hashmap-by-values

    public static LinkedHashMap<String, Integer> sortHashMapByValues(LinkedHashMap<String, Integer> passedMap) {
        List<String> mapKeys = new ArrayList<>(passedMap.keySet());
        List<Integer> mapValues = new ArrayList<>(passedMap.values());
        Collections.sort(mapValues, Collections.reverseOrder());
        Collections.sort(mapKeys);
        LinkedHashMap<String, Integer> sortedMap = new LinkedHashMap<>();

        Iterator<Integer> valueIt = mapValues.iterator();
        while (valueIt.hasNext()) {
            Integer val = valueIt.next();
            Iterator<String> keyIt = mapKeys.iterator();

            while (keyIt.hasNext()) {
                String key = keyIt.next();
                Integer comp1 = passedMap.get(key);
                Integer comp2 = val;

                if (comp1.equals(comp2)) {
                    keyIt.remove();
                    sortedMap.put(key, val);
                    break;
                }
            }
        }
        return sortedMap;
    }
}
Java Geo
  • 149
  • 2
  • 15