1

My program takes a string as input from the user. This method in particular gives back every unique character in the string and the amount of times it has been repeated. The output is sorted in ASCII order (lowest ASCII value first). Is there a way to sort this list in accordance with the number of times each character appears (i.e. most common character first, least common character last)? Moreover, is there a way, in the event that two characters appear the same amount of times, that the first character on the ASCII table is listed first? This is the code I'm trying to work off of.

public static void alphabeticalSort(String input)
{
    int[] ascii = new int[256];

    for (int i = 0; i < input.length(); i++) {
        char current = input.charAt(i);
        ascii[(int)current]++;
    }

    System.out.println(IntStream.range(0, ascii.length).filter(x -> ascii[x] > 0).mapToObj(x -> String.format("%s: freq %s", (char) x, ascii[x])).collect(Collectors.joining("\n")));
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • Your first `IntStream` does nothing FYI. – shmosel Oct 23 '17 at 05:43
  • What you're asking for makes no sense. If you sort the array, your characters cease to exist. You'll need to use a map or something. – shmosel Oct 23 '17 at 05:45
  • FWIW, sorting an array is very easy: `Arrays.sort(ascii);` – shmosel Oct 23 '17 at 05:48
  • What do you mean that the characters would cease to exist? –  Oct 23 '17 at 05:49
  • If you identify characters by index, you can't shuffle the values and still retain the relationship to their character. Try it and you'll quickly see what I mean. – shmosel Oct 23 '17 at 05:50
  • Their order isn't what matters for this method, though. So long as the same amount of characters are present, I wouldn't see the problem. And for the Arrays.sort() method, would it be possible to take the amount of times that each character appeared, put them into an array, then sort the array with Arrays.sort()? –  Oct 23 '17 at 05:53
  • You already have an array. Nothing's stopping you from sorting it, except that the order *does* matter. It's crucial to the characters' very identity, since each index represents a corresponding ASCII value. – shmosel Oct 23 '17 at 05:56
  • Oh, sorry. I see what you mean now. So there isn't a way to do this in the way I described? I'm not really familiar with HashMaps so I try to stay away from them. –  Oct 23 '17 at 06:04
  • Your `int[256]` with counts doesn't get you started in the right direction. (BTW—ASCII only has 128 codepoints and Java doesn't use ASCII. See [java.lang.Character](https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html).) – Tom Blodget Oct 23 '17 at 16:34

2 Answers2

3

You don't need the char[] - just stream the input string, collect it with a grouping collector that counts the occurrences and sort the resulting map directly:

input.chars()
     .mapToObj(c -> Character.valueOf((char) c))
     .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
     .entrySet()
     .stream()
     .sorted(Map.Entry.<Character, Long> comparingByValue()                    
                      .reversed()
                      .thenComparing(Map.Entry.comparingByKey()))
     .map(e -> String.format("%s: freq %d", e.getKey(), e.getValue()))
     .forEach(System.out::println); 
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • Or `mapToObj(c -> (char)c)` – shmosel Oct 23 '17 at 06:21
  • @shmosel yup, that would work. I prefer the explicitness of `Character.valueOf`, but I guess it's a matter of taste more than anything else. – Mureinik Oct 23 '17 at 06:22
  • I think `mapToObj` is already quite explicit, but to each their own, I guess. – shmosel Oct 23 '17 at 06:23
  • This works well for characters in the BMP range (`!Character.isSurrogate(c)`) in strings without combining characters (`Character.GetType(c) != Character.NON_SPACING_MARK`). So, no and no O̝. – Tom Blodget Oct 23 '17 at 16:52
0

I have a little different approach towards you rproblem but it may help you. You can create your own pair to count the frequency and then use compare to sort it using the frequency and in case frequncy matches then using char. Here's to count the frequency.

    public class Pair
    {
        private char letter;
        private int freq;
        public Pair(char letter, int freq)
        {
            this.letter = letter;
            this.freq= freq;
        }
        public char getLetter(){
            return letter;
        }
        public int getFreq(){
            return freq;
        }
    }
    public static int countFreq(String str, char c)
    {
        int count = 0;
        for(int i = 0; i < str.length(); i++)
        {
            if(str.charAt(i) == c) 
                count++;
        }
        return count;
    }

call this method to create pair of letter and frequency

    public static List<Pair> createPair(String str)
    {
        String s = str;
        List<Pair> list = new ArrayList<Pair>();
        while(s.length() != 0)
        {
            list.add(new Pair(s.charAt(0), countFreq(s,s.charAt(0))));
            s.replaceAll(s.charAt(0),"");
        }
         return list;
    }

you can then use stable sort out the pair using letter and then using frequnecy. There may be error in code as I have not tested it out, as it's to give you the general you my general idea.
You can refer here also for more ideas.
For sorting here are various options for stable sorting. In java by Colections.sort uses stable sorting so you shouldn't have problem in that. Other than that you can use compare fucntion for the help. Hope it gives you general perception