Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A program that counts the letters in string in Java

Tags:

java

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.

like image 839
Sisi Avatar asked May 22 '16 15:05

Sisi


3 Answers

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 :-/

like image 150
Aaron Avatar answered Nov 08 '22 22:11

Aaron


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

like image 1
Demur Rumed Avatar answered Nov 08 '22 23:11

Demur Rumed


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;
    }
}
like image 1
Java Geo Avatar answered Nov 08 '22 23:11

Java Geo