[Question]
  [
Given an input sentence consisting of one or more words `[a-z]+` and zero or more spaces , output an ASCII-art histogram (bar graph) of the letter distribution of the input sentence.
The histogram must be laid out horizontally, i.e. with the letter key along the bottom in alphabetical order from left to right, with a Y-axis labeled `1-` and every 5 units. The Y-axis must be the smallest multiple of five that is at least as tall as the tallest bar, and must be right-aligned. The X-axis is labeled with the input letters, with no gaps between. For example, input `a bb dd` should have label `abd` and not `ab d`, skipping the `c`. The bars themselves can be made of any consistent ASCII character -- I'll be using `X` here in my examples.
```
test example
5-
   X
   X   X
1-XXXXXXXX
  aelmpstx
```
Since there are three `e`, two `t`, and one of `almsx`.
**More examples:**
```
the quick brown fox jumped over the lazy dogs
5-
      X         X
      X         X
     XX  X      X  X XX
1-XXXXXXXXXXXXXXXXXXXXXXXXXX
  abcdefghijklmnopqrstuvwxyz
now is the time for all good men to come to the aid of their country
10-
              X
              X
              X  X
      X       X  X
 5-   X       X  X
      X   X   X  X
      X  XX XXXX X
   XXXXX XXXXXXX X
 1-XXXXXXXXXXXXXXXXXX
   acdefghilmnorstuwy
a bb ccc dddddddddddd
15-
      X
      X
10-   X
      X
      X
      X
      X
 5-   X
      X
     XX
    XXX
 1-XXXX
   abcd
a bb ccccc
5-  X
    X
    X
   XX
1-XXX
  abc
```
## I/O and Rules
* Input can be taken in any reasonable format and [by any convenient method](http://meta.codegolf.stackexchange.com/q/2447/42963). This also means you can take input in all-uppercase, if that makes more sense for your code.
* Leading/trailing newlines or other whitespace are optional, provided that the characters line up appropriately.
* Either a full program or a function are acceptable. If a function, you can return the output rather than printing it.
* Output can be to the console, returned as a list of strings, returned as a single string, etc.
* [Standard loopholes](http://meta.codegolf.stackexchange.com/q/1061/42963) are forbidden.
* This is [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'") so all usual golfing rules apply, and the shortest code (in bytes) wins.
  
[Answer]
# [Stax](https://github.com/tomtheisen/stax), 37 [bytes](https://github.com/tomtheisen/stax/blob/master/docs/packed.md#packed-stax)
```
ü╣úwóΓµ┐Wh0íJ▌Ñìs┤►ï╖öz<à↔/Ü@τ|:╢Ω$‼φ
```
[Run and debug it](https://staxlang.xyz/#p=81b9a377a2e2e6bf576830a14adda58d73b4108bb7947a3c851d2f9a40e77c3ab6ea2413ed&i=test+example%0Athe+quick+brown+fox+jumped+over+the+lazy+dogs%0Anow+is+the+time+for+all+good+men+to+come+to+the+aid+of+their+country%0Aa+bb+ccc+dddddddddddd%0A&a=1&m=2)
[Answer]
# [R](https://www.r-project.org/), ~~239~~ 230 bytes
```
K=table(el(strsplit(gsub(" ","",scan(,"")),"")));m=matrix(" ",L<-sum(K|1)+1,M<-(M=max(K))+-M%%5+1);m[2:L,M]=names(K);m[1,M-g]=paste0(g<-c(1,seq(5,M,5)),"-");m[1,]=format(m[1,],j="r");for(X in 2:L)m[X,M-1:K[X-1]]=0;write(m,1,L,,"")
```
[Try it online!](https://tio.run/##JY7dioMwEIVfZQgUJphAs@BN2zyBei@IF7FNbRZjukmkLey7u6N7M3/fmTMT18kN0cQPepsf4Zb4WulshsminTDlmJ6TyzimZUAGTDAm0tXMSAXne@Bnr73J0b13QX2RafFY/SpeKNFcJDaE31hxXsjmcCgLRRvd16kWTa9n420iRhMSy7HXT5OyPeJ4kVdUItkfLEUjyu2YZP@6Xt9DpJO4N@Jbs0iEZtiCm4Gsue9aslOnqmul6nt9PL@iyxa9UKIW298rm8MLXIL8sJCdt0AGYKYJxhBu4O0MOcA1EKC8iYy7QbhvpYsEljnHD1v/AA "R – Try It Online")
`table` does the heavy lifting here, uniquifying the characters, sorting them, and returning their counts.
Everything else is just ensuring the offsets are right for printing, which is the "real" work of an ascii-art challenge.
Thanks to [@dylnan](https://codegolf.stackexchange.com/users/75553/dylnan) for pointing out a bug.
Thanks to [@rturnbull](https://codegolf.stackexchange.com/users/59052/rturnbull) for the `scan` approach, dropping 2 bytes. 
[Answer]
# [Dyalog APL](https://www.dyalog.com/), ~~109~~ ~~97~~ ~~96~~ ~~95~~ ~~93~~ 88 bytes
```
{⊖(⍉r),⍨⍕↑(⊂'')@{1@0~⍵∊1,5×⍵}⍳≢⍉↑r←↑r,⍨⊂' -','   - '⍴⍨5×⌈5÷⍨≢1↓⍉↑r←↓{⍺,∊⍕¨0×⍵}⌸⍵[⍋⍵]∩⎕A}
```
[Try it online!](https://tio.run/##jY4xT8JQFIV3f8XdHiQlaQd22teC1bZPSyuicSAxuJBo2EyDg0PTVp7REHBnwlmNxlH@yf0j9bZoUo2Db3jn5p37nfMGF6PG6eVgdH6W4@3cFhjfqfmQ7gizRQ1lOq4rKFco5xjf1zC7ZqzeirSWeoXyBZNMU5rrBxonKJ8wXRJAe2PiCylJQqDBFAYADWAon@mxYKZJc/1aLKRLDeNZlZxFKN8VSqfaj5X6VTB9IzlGeUNygskj/Vef5PkQWLBtwX5o810wfNHzoC0OYSd09ywTxIHlQ@E7@lEfTNHpsi0iPNEDu1sage1aRPigOw50hDDBtTwIBHBBBmmxpNsU1S5G2ycj9AK/Xwb9qN50i0r77/rv/j@xf3A6GAZwzsGsnI1jcPYJ "APL (Dyalog Unicode) – Try It Online")
Requires `⎕IO←0`
*Way* too many bytes saved thanks to [Adám](https://codegolf.stackexchange.com/users/43319/ad%C3%A1m) and [Cows quack](https://codegolf.stackexchange.com/users/41805/cows-quack)!
[Answer]
# [05AB1E](https://github.com/Adriandmen/05AB1E), ~~58~~ 47 bytes
```
áê©S¢Z5‰`Ā+L5*1¸ì'-«ð5×ý#À¦Áí.Bís'X×ζ‚ζJR»,®3ú,
```
[Try it online!](https://tio.run/##MzBNTDJM/f//8MLDqw6tDD60KMr0UcOGhCMN2j6mWoaHdhxeo657aPXhDaaHpx/eq3y44dCyw42H1@o5HV5brB5xePq5bY8aZp3b5hV0aLfOoXXGh3fp/P@fkV@ukFuanKFQnp@fAiRKc1IUEsGc5IzS5GwFCJmZhioIVgZhg0TtAQ "05AB1E – Try It Online")
-11 bytes thanks to @Emigna
[Answer]
# gnu [sed](https://www.gnu.org/software/sed/) -r, ~~516~~ ~~490~~ ~~278~~ 249 + 1 bytes
```
s/$/:ABCDEFGHIJKLMNOPQRSTUVWXYZ /
:a
s/(.)(:.*\1)/\2\1/I
ta
s/[A-Z]+/ /g
h
z
:b
s/ \w/ /g
G
s/:/&I/g
/:II{5}+ *$/M!bb
s/[a-z]/X/g
G
s/:(I{5}+|I)\b/0\1-/g
s/:I*/  /g
s/ (\w)\1*/\1/g
s/$/; 10123456789I0/
:c
s/(.)I(.*\1(I?.))|;.*/\3\2/
/\nI/s/^/ /Mg
tc
```
[Try it online!](https://tio.run/##TY5ZU8IwFIXf8yuuM4zTwrSXgriUBwc3jIr7ghCcadNQq6XBNsgi/nVriqNjXnLud05yTyYCK0wmeZ5hCd3W3v7B4VH7mJ6cnnXOLy6vrm9u7@4fuo89QOJ6JEPDNg3XLjPHRFZjDlKiCtxvWb1BBQFD8kwWxPU1AzZdgbbWLq5TLdGl9KPxWYFyCTtrfpHqe9ZigN3fnLHyl9RkPlaZY2muKS0jwEqCwaYmc8qodxdzCZvgVJ1afaOxubW9Q6u6J//pSY2ip0F3bdNcNm39pM5qSJAlFDN80t06IVE8z5XIFIiZNxrHgqhnAW@TiL@Cn8ppAkM5g5fJaCwCkO8ihcKPvcUcAhlmJJFTiLIVVNFI6HQKXhxDKGUAI5GAksClNvRdhLxIfzMsZJRqY5KodE488H3gnEPw7/xRzr/kWEUyyXIr/QY "sed – Try It Online")
---
I am sure this can be improved~~, but for now, this should be good considering it is made in sed, where you don't have native arithmetic or sorting.~~ So I lied, this wasn't good enough, so I improved (rewrote) it by another 212 bytes, with a tip regarding the sorting algorithm from [Cows quack](https://codegolf.stackexchange.com/users/41805/cows-quack), which gave me a idea to make the unary to decimal conversion shorter too.  
Description of inner workings:
```
s/$/:ABCDEFGHIJKLMNOPQRSTUVWXYZ /
:a
s/(.)(:.*\1)/\2\1/I
ta
s/[A-Z]+/ /g
h
z
```
This sorts the input and separates the groups with spaces. This works by first appending an uppercase alphabet plus space separated by a colon to the end. Then it moves each character in front of the colon to a matching character behind the colon using a case-insensitive substitution in a loop. The uppercase letters are then replaced by spaces and the string is copied to the holding space.
```
:b
s/ \w/ /g
G
s/:/&I/g
/:II{5}+ *$/M!bb
```
This loop works by reducing each character group size by one, appending the sorted original line and incrementing unary counters after the colon that remained from the sorting. It loops until an empty line with a number of 5\*n + 1 is reached (since the last line ultimately results in whitespace). The pattern space looks something like this after the loop:
```
:IIIIII           
:IIIII           
:IIII           
:III  e         
:II  ee     t    
:I a eee l m p s tt x   
```
Then the formatting follows:
```
s/[a-z]/X/g            # makes bars consistent
G                      # appends line that becomes x-axis
s/:(I{5}+|I)\b/0\1-/g  # moves zero in front of line 1 or 5-divisible
                       # lines for the decimal conversion and adds -
s/:I*/  /g             # removes numbers from other lines
s/ (\w)\1*/\1/g        # collapses groups of at least 1 into 1
                       # character, deleting the space before it
                       # so that only size-0-groups have spaces
```
And finally, the unary to decimal converter remains:
```
s/$/; 10123456789I0/
:c
s/(.)I(.*\1(I?.))|;.*/\3\2/
/\nI/s/^/ /Mg
tc
```
It basically appends a string where the knowledge of conversion is. You can interprete it as :space:->1 and 0->1->2->3->4->5->6->7->8->9->I0. The substitution expression `s/(.)I(.*\1(I?.))|;.*/\3\2/` works similar to the sorting one, replacing the characters in front of I's [ `(.)I` ] by the character that is next to the one from the front of the I in the conversion string [ `(.*\1(I?.))` ] and if there is no I left, it removes the appended string [ `|;.*` ]. The substitution [ `/\nI/s/^/ /Mg` ] adds padding if needed.
Thanks to [Cows quack](https://codegolf.stackexchange.com/users/41805/cows-quack) for reducing the size by 26 bytes and for the shorter sorting algorithm.
[Answer]
# [Python 2](https://docs.python.org/2/), 192 bytes
```
s=input()
d={c:s.count(c)for c in s if' '<c}
h=-max(d.values())/5*-5
for y in range(h,-1,-1):print('%d-'%y*(y%5==2>>y)).rjust(len(`-h`))+''.join(('X '[y>v],k)[y<1]for k,v in sorted(d.items()))
```
[Try it online!](https://tio.run/##HY/RioMwEEXf/Yp5kWRadWnBl9L4HQulUEliTasZSaK7Ydlvd5OFgRm4hzv3LjGMZM/77oWxyxo4Fkr8yItvJK02cIkDOZBgLHgwAwN2lb/FKOq5/@aq2fpp1Z4jfrSHui0yGzPrevvUfKzqUxq8LM4kL1aqmpXxwGPZCnHuuojYuNfqA5@05Y96fCAeGWteZCzn7BPYLXbbvXrjLV5P9@z@rrb/LOSCVimACXrO/3HfmaUvMB7CqCGYWUPm@2mCJ5GCWVsIBJKSkHaGeqOAhnyaVDHXdZH9AQ "Python 2 – Try It Online")
## Explanation
Line 2 computes the histogram values in a fairly straightforward way, discarding `' '`.
Line 3 uses the trick of computing `ceil(x/5)` as `-(-x/5)`: we round the maximal frequency up to the next multiple of 5 using the formula `-x/5*-5`. This is `h`.
Line 4 is a loop counting from `h` down to `0` inclusive, printing each row:
* If `y%5==2>>y` we print a label. This is when `y` ∈ {1, 5, 10, 15, 20, …}
(This formula could maybe be shorter. We just need something that's **1** or **True** for {1, 5, 10, …}, and **0** or **False** or even a negative integer for all other values of `y`.)
* We right-justify the label (or empty space) into `len(`-h`)` spaces: this is a neat one-byte saving over `len(`h`)+1`!
* Then, we print either `X`'s and spaces for this row (if `y` ≥ 1) or the letters (if `y` = 0), running through key-value pairs in `d` in ascending order.
[Answer]
# Pyth, 65 bytes
```
J.tm+ed*hd\Xr8S-Qd)=+J*]d%_tlJ5_.e+?q<k2%k5.F"{:{}d}-",klQ*dhlQbJ
```
[Try it here](http://pyth.herokuapp.com/?code=J.tm%2Bed%2ahd%5CXr8S-Qd%29%3D%2BJ%2a%5Dd%25_tlJ5_.e%2B%3Fq%3Ck2%25k5.F%22%7B%3A%7B%7Dd%7D-%22%2CklQ%2adhlQbJ&input=%22now+is+the+time+for+all+good+men+to+come+to+the+aid+of+their+country%22&debug=0)
### Explanation
```
J.tm+ed*hd\Xr8S-Qd)=+J*]d%_tlJ5_.e+?q<k2%k5.F"{:{}d}-",klQ*dhlQbJ
J.tm+ed*hd\Xr8S-Qd)
     Get the bars.
                   =+J*]d%_tlJ5
     Round up the height to the next number that's 1 mod 5.
                               _.e+?q<k2%k5.F"{:{}d}-",klQ*dhlQbJ
     Stick the axis labels on.
```
[Answer]
# [Charcoal](https://github.com/somebody1234/Charcoal), 62 bytes
```
≔Ｅ²⁷⟦⟧ηＦθ⊞§η⌕βιι≔⊟ηθ≦ＬηＰ⭆β⎇§ηκιω↑↑ΦηιＦ÷⁺⁹⌈η⁵«≔∨×⁵ι¹ιＪ±¹±ι←⮌⁺ι-
```
[Try it online!](https://tio.run/##TY9BSwMxEIXP3V8x9DSB9KBQRD0VRKi0umg9iYfYne4Gs8k2yW5bxN@@TmoEL5nhzcx7X7aN8lunzDguQtC1xbXq8PJKwtu7kNCI22LnPOBeQNmHBhdxaSs6YiPhXtsKPyRoIdJzW2SD0nXYsLRnic2yuiJbx@bXcd2bqDuvbcSXyKVOmey0IW@VP/0P@UzeEg5CpDs3EN68dtyW52vuE4eJ5NOyFn@4Sxvv9KArwtL0Aa8lrNVRt33LZOw4FwK@iklGe/K40S0FnCcLCRf5P5OHvu02Dh@pVpEwybk9B00yw4p2UcIzDeRDzmPi6Wx6Zv4eR@sOoAPEhiByDiRCZQzUzlXQkoXoYOt4wDUtKV2B26VWex70NvpTMc4G8wM "Charcoal – Try It Online") Link is to verbose version of code. Explanation:
```
≔Ｅ²⁷⟦⟧η
```
Create a list of 27 lists.
```
Ｆθ⊞§η⌕βιι
```
Push each input character to the list corresponding to its position in the lowercase alphabet. Non-lowercase characters get pushed to the 27th list.
```
≔⊟ηθ
```
Discard the 27th element of the list.
```
≦Ｌη
```
Take the lengths of all the elements of the list.
```
Ｐ⭆β⎇§ηκιω
```
Print the lowercase letters corresponding to non-zero list elements.
```
↑↑Φηι
```
Print the non-zero list elements upwards. Since this is an array of integers, each integer prints as a (now vertical) line, each in a separate column.
```
Ｆ÷⁺⁹⌈η⁵«
```
Calculate the number of tick marks on the Y-axis and loop over them.
```
≔∨×⁵ι¹ι
```
Calculate the position of the next tick mark.
```
Ｊ±¹±ι
```
Jump to the next tickmark.
```
←⮌⁺ι-
```
Print the tickmark reversed and back-to-front, effectively right-aligning it.
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), 48 [bytes](https://github.com/DennisMitchell/jelly/wiki/Code-page)
What a mine-field to traverse!
```
J’⁶D;”-Ɗ%5^ỊƲ?€Uz⁶ZU
ḟ⁶ṢµĠ¬;⁶$L%5Ɗ¿€;"@Qz⁶Ç;"$ṚY
```
A full-program printing the result (as a monadic link it would return a list containing characters and integers from `[0,9]`)
**[Try it online!](https://tio.run/##y0rNyan8/9/rUcPMR43bXKwfNczVPdalahr3cHfXsU32j5rWhFYBJaJCuR7umA9kPNy56NDWIwsOrbEGclR8VE2PdR3aD1RlreQQCFJ4uN1aSeXhzlmR////z8svV8gsVijJSFUoycxNVUjLL1JIzMlRSM/PT1HITc1TKMlXSM4HSgBpkKLEzBSF/DQQM7MIKFGaV1JUCQA "Jelly – Try It Online")** Or see the [test-suite](https://tio.run/##TYyxTsMwEIZ3nuJktRuMnTLAwIRYGDoUBJLjuMXBiYvj0KZTy1IpbwASAjF3QiKiAlHJLXmP@EXMhYmTTvfffZ8u5lIW3p@4@YNbVMeBmz8d1GW3d9V8lvXbobtf9WcIzvt7zcczhmb9at93L3YV4NI57fbq0m7QCsjRWStulwHpNOvHgf/52i4RDLBjt/i2la28vyCGZwb4lCZjyck@MdccbnPBbiDUapLCUE0hzpMxj0DdcQ0tl3RWQKRGGfqpmoDI/s5GJBx9DVRKGCkVQcJTMAqYQoCzlajAR8M2Co0gT40u8A2FMATGGET/ilz@Ag "Jelly – Try It Online")
### How?
```
J’⁶D;”-Ɗ%5^ỊƲ?€Uz⁶ZU - Link 1, get y-axis: list of columns (including x-axis & top-spaces)
J                    - range of length  [1,2,3,4,5,6,...,height+1] (+1 for x-axis)
 ’                   - decrement        [0,1,2,3,4,5,...] (line it up with the content)
             ?€      - if for €ach...
            Ʋ        - ...condition: last four links as a monad:
        %5           -   modulo by five
           Ị         -   insignificant? (1 for 0 and 1, else 0)
          ^          -   XOR (0 for 1 or multiples of 5 greater than 0, else 0)
  ⁶                  - ...then: literal space character
       Ɗ             - ...else: last three links as a monad:
   D                 -   decimal list of the number, e.g. 10 -> [1,0]
     ”-              -   literal '-' character
    ;                -   concatenate, e.g. [1,0,'-']
               U     - upend (reverse each)
                z⁶   - transpose with a filler of space characters
                  Z  - transpose
                   U - upend (i.e. Uz⁶ZU pads the left with spaces as needed)
ḟ⁶ṢµĠ¬;⁶$L%5Ɗ¿€;"@Qz⁶Ç;"$ṚY - Main link: list of characters
ḟ⁶                          - filter out space characters
  Ṣ                         - sort
   µ                        - start a new monadic chain, call that S
    Ġ                       - group indices of S by their values
     ¬                      - logical NOT (vectorises) (getting 0 for the X "characters")
             ¿€             - while for €ach...
            Ɗ               - ...condition: last three links as a monad:
         L                  -   length
          %5                -   modulo by five
        $                   - ...do: last two links as a monad:
      ;⁶                    -   concatenate a space character
                  Q         - deduplicate S (get the x-axis)
               ;"@          - zip with (") concatenation (;) with swapped arguments (@)
                   z⁶       - transpose a with filler of space characters
                        $   - last two links as a monad:
                     Ç      -   call last link (1) as a monad (get y-axis)
                      ;"    -   zip with concatenation (complete the layout)
                         Ṛ  - reverse (otherwise it'll be upside-down)
                          Y - join with newlines
                            - implicit print
```
[Answer]
# [APL (Dyalog Classic)](https://www.dyalog.com/), 56 bytes
```
⊖h,⍨{∊⍺'-'/⍨~×5|⍵-⍵<2}⌸⍕⍪⍳≢h←(⊢↑⍨≢+5|1-≢)⍉(⊢,⌸⊣\)∊⍞∘∩¨⎕a
```
[Try it online!](https://tio.run/##TU1NT8JAEL33V8wNiWz8SLh52bYLrrZd3LYCxkuDQUiaYMLJiB6xNC7RA@rJA17QqzExHvWfzB@pU05OMpl58968l1yk7OwySUfnrJcm4/GwV@B8MRzh9H7bwuy2X2D@OKihWV1hlqP5rrDKFqGb36f6BM0no97bvca7LzQLNO9oPnC2HND7BuZLnD6Qlg6b9ckOo1lFMyuJWvmQv55W16YvmD1j9vazouikoNSib0UijEB0uN/yhEVwX8BRLJ1DsLVqB9BQHTiI/ZZwQR0LDSXv8ZMuuKoZkj5QbZDh@hxJX5BeA/c8aCrlgi8CiBQ4igiapYhLMmqUq9RExEGku2TDwbbBcRxw/9Uf "APL (Dyalog Classic) – Try It Online")
[Answer]
# [Ruby](https://www.ruby-lang.org/), ~~250~~ ~~248~~ ~~234~~ ~~188~~ ~~173~~ ~~157~~ 153 bytes
```
->s{a=s.scan(/\w/).sort|[]
m=-(c=a.map{|l|s.count l}).max/5*-5
m.downto(1).map{|i|(i%5<1||i<2?"#{i}-":'').rjust(m)+c.map{|l|l<i ?' ':?X}*''}<<' '*m+a*''}
```
[Try it online!](https://tio.run/##TY9RT4MwFIXf@RUnM6bABsuMS8wC8jdM5h5KKdJJW2yLbK78dgSNiffl3u/ec05yTV9epzqfkmd7o7lNLaMq3L4O2yi12jh/PAUyT0KW01TS7uZbb1Ome@XQjtG8umz3cbIPZFrpQTkd7qJfnfChuN9nO@9F9lCs7m5iTFYHQqLUnHvrQhmt2V9imwkUBORQvIwxIWOWzRDLNV1goshxJI5bB36hsms52YCc63e8aQd5xWdj0TFQVeHSGgy8xEcvvhaV0gOEhWs4nJActTagbTs7dQXJFZwG0/Nh7ouIigq6XkZh8POmuS45FGUJxhiqf0VOAU05ZQ0qDW@d8QHQ9c6iPs502qBI4sengKtq@gY "Ruby – Try It Online")
Thanks to:
* [dylnan](https://codegolf.stackexchange.com/a/161934/70753) for -16 bytes with less strict padding
* [Lynn](https://codegolf.stackexchange.com/a/161943/70753) for -2 bytes by rounding up with `-x/5*-5`
* [Kirill L.](https://codegolf.stackexchange.com/questions/161921/alphabet-histogram#comment392424_161978) for -2 bytes by getting unique array elements with `|[]`
[Answer]
# [Java (JDK)](http://jdk.java.net/), 295 bytes
```
s->{int d[]=new int[26],m=0;char a;for(int c:s.getBytes())m=c>32&&++d[c-=65]>m?(d[c]+4)/5*5:m;String r=m+"-",z=r.replaceAll("."," ");for(;m>0;r+="\n"+(--m%5>0&m!=1|m<1?z:z.format("%"+~-z.length()+"s-",m)))for(a=0;a<26;a++)r+=d[a]>0?m>d[a]?" ":"x":"";for(a=64;a++<90;)r+=d[a-65]>0?a:"";return r;}
```
[Try it online!](https://tio.run/##jZJNj5swEIbv@yumlnZlSnDZdBOpEIjaSiv1UPWw6inl4BiHkPUHtU02yTb969ROcmgvUZHMDH6fGcPLbOiWJpv6eej6pWgZMEGtha@0VfB6A3DZtY46H7a6rUF6DT8506pmUQE1jY1OKMDG9yK9awVZ9Yq5VivyeElm54LROZSwggIGm5SvrXJQL6pC8Rfw@WI8rUaySHO2pgZovtIGB4RlljTcfdo7bnEUyYKV78d3d3FcL1hSTCdVKefY51X8EL2bvJ1kMj8fBaaQMUrQ6FAYYngnKOMfhcCIoBECFJ1OyGWZ5iYu0A@FYpwk8nZSpnfyTXH/S87u54fsQDwmqcPoFsW/kwMRXDVujaMYWd9bRlEU@lD/4nQ2nuY0jiPfr17QqkznsgzJ3B@XoZ1fKD/D04cAzj6k@QVOwoekcxoQw11vFJj8OOQnd0PNNphimuwf2yE8eT/9nTj9veu4@Uwtx1F@kZ/21nFJdO9I5z1xQmH0RXW9ywBBHOquot96F1h0DVoR2nVij0Ova9x/aMebsI7D4H@1A76jshN8cGsOP/uWPcPS6Bfl3djBppcdr0FvuYGgC3rYQ60bOyjtp8meNl0refAOqBDQaO0HmCtwGpj2go8Bon6u9SqkrfFCr5zZDxSWS2CMQf3X9Qc "Java (JDK) – Try It Online")
## Credits
* -3 bytes thanks to [Kevin Cruijssen](https://codegolf.stackexchange.com/users/52210/kevin-cruijssen)
* -1 byte thanks to [ceilingcat](https://codegolf.stackexchange.com/users/52904/ceilingcat)
[Answer]
# [JavaScript (Node.js)](https://nodejs.org), 262 256 bytes
\*Thanks to @Shaggy for reducing by 2 bytes
```
a=>[...a].map(x=>x>" "&&(d=c[x]=(c[x]||x)+"X")[m]?m=d.length-1:0,c={},m=i=0)&&Object.keys(c).sort().map(x=>[...c[x].padEnd(m)].map((l,j)=>A[m-j-1]+=l),A=[...Array(m+=6-m%5)].map(x=>(++i>=m||((D=m-i)%5&&m-i^1)?"":D+"-").padStart((m+" ").length)))&&A.join`
`
```
[Try it online!](https://tio.run/##hU@7TgMxEOz5ipWlnLy6nEUKKJB8KBLUFDRIURCO7QQffhw@By4Qvj3YvAQVLvahmZ0Zd@JJDDKaPjU@KH1Y84Pg7YIxJpbMiZ6OvB1bAqSqqOJyMS45LXW/H7EmNwQXbnnuuGJW@026b2Znx1PJX9@mjht@jFV1teq0TOxB7wYqkQ0hJorfysWnqLFeqEuvqMNPU2qnHfJ2vnBN18yWNbc4nfPCnscodtTV/LRxkxP8yUjr2rTc7feUXnDXGJycVFXutzM8J@TsoiYNwWJznUROkBXyn/ArNWIOOmddMP7u6O4ggx@C1cyGDV1TkvSQQI/C9VYTxKPf8N@tkO81PG6NfIBVDM8e1mGEbut6rSA86QgFt@JlBypshn/VfHgGM3wcJeN0VosgrIVNCAqc9pACyJCB3AtJmGyzLqOJGdj6FHf/mghYrUBKCerXy1eHdw "JavaScript (Node.js) – Try It Online")
[Answer]
# [Python 2](https://docs.python.org/2/), ~~249~~ ~~224~~ ~~219~~ ~~215~~ ~~205~~ ~~197~~ ~~187~~ ~~188~~ ~~182~~ 176 bytes
```
def f(s):S=sorted(set(s)^{' '});C=map(s.count,S);P=max(C)+4;return zip(*(zip(*[('%d-'%y*(y%5==2>>y)).rjust(P)for y in range(P,0,-1)])+[(n*'#').rjust(P)for n in C]))+[[' ']*P+S]
```
[Try it online!](https://tio.run/##dVHLbsMgELznK1aqIljnoTZqL4mcS37AUo6uKxGDE1IbXMBNSNVvd8Htoa/sAQZmdmYRrXcHrRb9DRnzGRn7hPrxQ5ou1muPPRcVVNTicptabZzg1AoXzk9vBMg7rjZpw1pq56XulJtucZWFizPd4OR@ZYTrjIKLbGlChzWn/2Tg3Bw762iGlTbgQSowTO0Fzaa309kdFjjJqUrIDfmpVFG5KTDQeRimSLLJtujbkOo8pFCzZscZ2CWQR0XmRy0VzckXqKUSCNElomgUHxmSonY0ao1UDj6tKGGw20EZi@BV6i/nDgJeOlk@w87oU0jQZzh2TSs46FdhIPI1u3jgem8Jwq92pU8g7aByshHDsKyuYa81h0YocBpKHYiwRxGTwbeKUBoYvsP4q/MC/1YE@w8 "Python 2 – Try It Online")
Returns a list of lists of characters representing lines.
* Saved some bytes by including a lot of extra whitespace.
* Had an unnecessary `map(list,yticks)` in there.
* Changed space padding to save some bytes.
* I thought I was sorting but I was not: +2 bytes. But I saved one independently at the same time. `y==1` replaced by `y<2`.
* -6 bytes thanks to [Lynn](https://codegolf.stackexchange.com/a/161943/75553) by using `'%d-'%y*(y%5==2>>y)` instead of `(`y`+'-')*(not y%5or y<2)`.
Slightly ungolfed: 
```
def f(s):
	S=sorted(set(s)^{' '})  # sorted list of unique letters (without ' ')
	C=map(s.count,S)        # count of each unique letter in the input
	P=max(C)+4              # used for padding and getting highest y tick
	B=[(n*'#').rjust(P)for n in C]     # create bars
	yticks = [('%d-'%y*(y%5==2>>y)).rjust(P)for y in range(P,0,-1)]  # create y ticks at 1 and multiples of 5
	yticks = zip(*yticks)                      # need y ticks as columns before combining with bars
	return zip(*(yticks+B))+[[' ']*P+S]        # zip ticks+bars then add row of sorted unique letters.
```
[Answer]
# [C# (.NET Core)](https://www.microsoft.com/net/core/platform), ~~344~~ ~~340~~ 338 + 18 bytes
Includes 18 bytes for `using System.Linq;`
Saved 6 bytes thanks to @KevinCruijssen.
```
n=>{var l=n.Where(c=>c!=32).GroupBy(c=>c).OrderBy(c=>c.Key).ToDictionary(k=>k.Key,c=>c.Count());int h=(l.Values.Max()/5+1)*5,o=(h+"").Length+1,m=l.Keys.Count+o,t=h+1,i=0,j;var a=new string[t];for(string p,q;i<t;a[i++]=q)for(q=(p=i>0&i%5<1|i==1?i+"-":"").PadLeft(j=o);j<m;){var c=l.ElementAt(j++-o).Key;q+=i<1?c:l[c]>=i?'X':' ';}return a;}
```
[Try it online!](https://tio.run/##dVJdc9MwEHyuf8XhGYiFXdPA9KWO0imlMAPt0AGGMlPyoMqXRKmtSyQ5rQn57UHKF@UBv9i63Vvvnk7aQ0kGV41VegRfW@uwzi@VnhVRJCthLVwbGhlRwyI6sE44JWFOqoQroXRinfFttwMQZmSZp0RbhfeNlr0NmsGO1Ych8JXm/cVcGKi4zm/GaDCRvC@f8TevWf7BUDN9264rLP9sSjTbU/4JW5Z/o3dKOkVamDa55/37UM7W@Dk12iWMFUo7GPOkyr@LqkGbX4nHhL06Trvs5XFGPBmncczyS9QjN067Wc2rIGI3Ailljoey4kfZpAg@Bdf4sMvgBsWQzDY3TLNZoXquELcqTQd8xgI248mUq/7RC/X8uNf9rTjvnqo0PoxPwn@vRXmJQ5dMOLFi0qsLth6G9C4uKqxRuzMPpukhsWCrmKVc9bqn8qS6lYM@V6edH52TDnSKpUHXGA2iWK78Xe1vwqF1Uli0wOGJ8QEsIHZjhFmj5D3cGXrQMKRHmDT1FEugORoIeCV@tVDSyMYZxJoeQNl13akafYOfR1XBiKgEbxYcgSQP@HcgCb8YNAyfymcKAzVt0BFwdwdSSiifPDEsi8gropBj2I10Zx@U/huFRYsI/LNdrnPSlirMb4xy6HcVk31XCvFPHbNiTd9HN2ibyvmBDPdMln9BH9liErbqzBjRJv@0ATVu2oSmzTn/SH7hg3q21dvS/2tqI@BZyyg6WEbL1R8 "C# (.NET Core) – Try It Online")
[Answer]
# [Bash](https://www.gnu.org/software/bash/) + coreutils, 332 324 323 318 312 302 298 296 293 291 bytes
```
c()(cut -d\  -f$@)
p=printf
cd `mktemp -d`
grep -o [^\ ]<<<$@|sort|uniq -c|c 7->a
sort -k2<a>b
r=$[`c 1 <a|sort -n|tail -1`+5]
s=${#r}
t()($p \ ;((i++<s))&&t;i=)
for((;--r;));{
((r%5&&r>1))&&t||$p %${s}s- $r;IFS='
'
for l in `<b`;{ ((r<=`c 1 <<<$l`))&&$p X||$p \ ;}
echo
}
t
c 2 <b|tr -d \\n
```
[Try it online!](https://tio.run/##JY@xbsIwEIb3e4qTaoIt6gEkxGAHMVXq3KUSoXLiEGIFktR2qFDMs6cGttP99@m@v8hdPU2aMqoHj7zMEHlFdgz6tLem9RXoEtWl8cdLH2MFJ3uMQ4f7nwwPUkqyC66zPgyt@UWug8YN3@bw2CFvVjLfFmBTslcalyjz8Ara4HNzRr5Ui/UBXErGN3sHHzVIjxkKSs1iIR1jSeKFSRlUnaVUcG4FY2IESu1snSR2u3yehBCxGRnd3XEkVnx@fKVzmD8oPKNpUclCiREjJtOXSTQ/qwccye8nH9/e4ajrDqIJaFyhLIK3sTVmWTtNU@7KKtb/c@Wpbi@NGW4nb291ca30tR6a938 "Bash – Try It Online")
Annotated:
```
c()(cut -d\  -f$@)
p=printf              # saving a few bytes
cd `mktemp -d`        # for temp files
grep -o [^\ ]<<<$@    # grabs all non-space characters
    |sort|uniq -c     # get character frequency
    |c 7->a           # slightly hacky way of stripping leading spaces;
                      #     uniq -c adds 6 spaces in front of each line
sort -k2<a>b          # store frequencies sorted alphabetically in b
r=$[`                 # r = highest frequency +5:
    c 1 <a            #     get frequencies
    |sort -n|tail -1  #     get maximum frequency
    `+5]              #     +4 so at least one multiple of 5 is
                      #     labeled, +1 because r gets pre-decremented
s=${#r}                    # s = length of r as a string
t()($p \ ;((i++<s))&&t;i=) # pad line with s+1 spaces
for((;--r;));{         # while (--r != 0)
    ((r%5&&r>1))&&     # if r isn't 1 or a multiple of 5
        t||            #     then indent line 
        $p %${s}s- $r; # otherwise print right-aligned "${r}-"
        IFS='
'                      # newline field separator
    for l in `<b`;{          # for all letters and their frequencies:
        ((r<=`c 1 <<<$l`))&& #     if frequency <= current height 
            $p X||           #         then print an X
            $p \ ;}          #     otherwise print a space
    echo
}
t # indent x-axis labels
c 2 <b|tr -d \\n # print alphabetically sorted characters
```
Thanks to @IanM\_Matrix for saving 3 bytes.
[Answer]
# C, 201 bytes
```
char c[256],*p,d;main(int a,char **b){for(p=b[1];*p;p++)++c[*p|32]>d&*p>64?d++:0;for(a=(d+4)/5*5;a+1;a--){printf(!a||a%5&&a!=1?"    ":"%3i-",a);for(d=96;++d>0;c[d]?putchar(a?32|c[d]>=a:d):0);puts(p);}}
```
Input is taken from the command line (first argument). Uses exclamation marks instead of X's to further reduce code size. Counter on the left is always three characters long.
Tested with GCC and clang.
[Answer]
# Excel VBA, 316 bytes
An Anonymous VBE immediate window function that takes input from cell `[A1]` and outputs to the VBE immediate window.
```
For i=1To 26:Cells(2,i)=Len(Replace([Upper(A1)],Chr(i+64),11))-[Len(A1)]:Next:m=-5*Int(-[Max(2:2)]/5):l=Len(m)+1:For i=-m To-1:?Right(Space(l) &IIf(i=-1Xor i Mod 5,"",-i &"-"),l);:For j=1To 26:?IIf(Cells(2,j),IIf(Cells(2, j) >= -i, "X", " "),"");:Next:?:Next:?Spc(l);:For i=1To 26:?IIf(Cells(2,i),Chr(i+96),"");:Next
```
### Ungolfed Version
```
Public Sub bar_graph()
    For i = 1 To 26
        ''  gather the count of the letter into cells
        Cells(2, i) = Len(Replace([Upper(A1)], Chr(i + 64), 11)) - [Len(A1)]
    Next
    m = -5 * Int(-[Max(2:2)] / 5)   ''  get max bar height
    l = Len(m) + 1                  ''  length of `m` + 1
    For i = -m To -1
        ''  print either a label or free space (y-axis)
        Debug.Print Right(Space(l) & IIf((i = -1) Xor i Mod 5, "", -i & "-"), l);
        For j = 1 To 26
            ''  print 'X' or ' ' IFF the count of the letter is positive
            If Cells(2, j) Then Debug.Print IIf(Cells(2, j) >= -i, "X", " ");
        Next
        Debug.Print                 ''  print a newline
    Next
    Debug.Print Spc(l);             ''  print spaces
    For i = 1 To 26
        ''  print the letters that were used (x-axis)
        Debug.Print IIf(Cells(2, i), Chr(i + 96), "");
    Next
End Sub
```
[Answer]
# [Perl 5](https://www.perl.org/) `-n`, ~~198~~ 168 bytes
```
s/[a-z]/$\<++${$&}?$\=${$&}:0/eg;$\++while$\%5;$;=1+length$\++;printf"%$;s".'%s'x26 .$/,$\%5&&$\-1?"":"$\-",map$$_>=$\?X:$$_&&$",a..z while--$\;say$"x$;,map$$_&&$_,a..z
```
[Try it online!](https://tio.run/##LY3baoQwFEV/5RCOzoPGS8F5MLV@wbwXmjKETtRATMRkmEvprzeN0qe9YC3Yi1x1E4IrPwR9fpbIX7MMvzH96ZF3O7RVKUeGPMtuk9ISedIwZF2daWlGP22CLasyfiAJMkeKQ@IO95cjFFjmW52myGndE9KSCCSfxYJ4fuuQ9@9tpOhJLoriCfsBpciZEw8kd2T/cUzOexKCsTdQDvwkwatZwmBXEFrDaO0FZmnAW/iyUcTdIqEuYIcN1RrF1fj18WsXr6xxgZ6aoqqrQM0f "Perl 5 – Try It Online")
[Answer]
# [Python 3](https://docs.python.org/3/), 177 bytes
```
lambda s:[[list(("%d-"%i*(i%5==2>>i)).rjust(len(q)))+["* "[s.count(c)<i]for c in q]for i in range(max(map(s.count,q))+4,0,-1)]+[[" "]*len(q)+q]for q in[sorted(set(s)-{' '})]][0]
```
[Try it online!](https://tio.run/##TVBBbsIwELz3FStLCC8JiJb2gho@YnwwjgOLEjvEiwqq@vbUDhxYaTUjzexo7P7Op@A3Y1Ptx9Z0h9pA3CrVUmQpxaxeihktJM2@qupjtyPE1XC@Jq11Xl4QsVBiAULFlQ1Xz9LiN@kmDGCBPFwmSpkOxh@d7MwtbS@f9jIlFJ/luly@oy6UEiD04pFcPG4v6VbFMLCrZXQsIy5/5zD/Q63VWo/sIsdKiYzgbqbrWydKED78AEXgkwOmzkGOMm0LxxBq6JwHDmBDEhJmk6EaQpMppeq52nDPOQYOB7DWQv0yr4K1Qr/ldM6PnOpsoR8ofQUn296L1TmQl@KJEacuMbsbyYgPE47/ "Python 3 – Try It Online")
This may not be the most byte-efficient approach in Python, but I really wanted to solve this with a "true one-liner" lambda.
Outputs a list of character lists. Abuses multiple leading newlines and spaces just like everybody else. It may actually be further reduced to 174 bytes if it is acceptable to wrap the result in another list, so that we could transfer the final `[0]` indexing to the footer.
[Answer]
# JavaScript (ES8), 200 bytes
Takes input as an array of characters. Returns a string.
```
s=>(s.sort().join``.replace(/(\w)\1*/g,s=>a.push(s[0]+'X'.repeat(l=s.length,h=h<l?l:h)),h=a=[]),g=y=>y--?(y<2^y%5?'':y+'-').padStart(`${h}_`.length)+a.map(r=>r[y]||' ').join``+`
`+g(y):'')(h+=5-~-h%5)
```
[Try it online!](https://tio.run/##dY5BTsMwEEX3PcUIUdkmjQtI3VR1ewg2SGkgbuLEKU4cbKetoXD14CCQWNDZzJf@05vZ8wO3uak7F7e6EEPJBsvW2FKrjcOE7nXdZhk1olM8F3iOt0eyvbuZV7OAcdr1VmKb3KYRekQjJbjDilmqRFs5OZNMrtRGLSUhIXOWpGRWMc/WPo432K/un/x0sUFo6SMUI0I7Xjw4Hi5n1@/y4zn78ZCI04Z32LC1SXx6PiNAv79F2SSLKuzJEiGCZcQW8Wcspwsy5Lq1WgmqdIVLnFBKr5ywDsSJN50SVykhk/8YKeC1r/MX2Bl9bKHUJ9j3TScK0AdhYOwVf/NQ6MpekrT6CLX9Zl3diCAxwJWCSusCGtGC05DrUIQ9QrwO9nKMtQlF3zrjL7k57HaQ5zkUf2aEhy8 "JavaScript (Node.js) – Try It Online")
### Commented
```
s => (                    // s[] = input array of characters (e.g. ['a','b','a','c','a'])
  s.sort()                // sort it in lexicographical order (--> ['a','a','a','b','c'])
  .join``                 // join it (--> 'aaabc')
  .replace(/(\w)\1*/g,    // for each run s of consecutive identical letters (e.g. 'aaa'):
    s => a.push(          //   push in a[]:
      s[0] +              //     the letter, which will appear on the X-axis
      'X'.repeat(         //     followed by 'X' repeated L times
        L = s.length,     //     where L is the length of the run (--> 'aXXX')
        h = h < L ? L : h //     keep track of h = highest value of L
    )),                   //   initialization:
    h = a = []            //     h = a = empty array (h is coerced to 0)
  ),                      // end of replace() (--> a = ['aXXX','bX','cX'] and h = 3)
  g = y =>                // g = recursive function taking y
    y-- ?                 //   decrement y; if there's still a row to process:
      (                   //     build the label for the Y-axis:
        y < 2 ^ y % 5 ?   //       if y != 1 and (y mod 5 != 0 or y = 0):
          ''              //         use an empty label
        :                 //       else:
          y + '-'         //         use a mark
      ).padStart(         //     pad the label with leading spaces,
        `${h}_`.length    //     using the length of the highest possible value of y
      ) +                 //     (padStart() is defined in ECMAScript 2017, aka ES8)
      a.map(r => r[y]     //     append the row,
                 || ' ')  //     padded with spaces when needed
      .join`` + `\n` +    //     join it and append a linefeed
      g(y)                //     append the result of a recursive call
    :                     //   else:
      ''                  //     stop recursion
)(h += 5 - ~-h % 5)       // call g() with h adjusted to the next multiple of 5 + 1
```
[Answer]
# [PHP](https://php.net/), 180 bytes
```
$c=count_chars($argn,1);unset($c[32]);for($s=max($c)+4;$s;){printf($s%5&&$s>1?$z='   ':'%2d-',$s--);foreach($c as$d)echo$d>$s?X:' ';echo"
";}foreach($c as$a=>$d)$z.=chr($a);echo$z;
```
[Try it online!](https://tio.run/##VZDNTsMwDMfvfQpr8tZVrEgbcFloe@MZkBBCWZKugTYOScq2Il6dkvbEfPDX/2dbsm3s@FjZ6FEUgnoT3kTDnV8jd0ez2WasN16FNYqXu91rxmpya/RFx8@xld3cM/Qs@7ZOm1BHYfmwWqEvtxUORQoA6T5d7mSebtDn@TytuGjiKHCPMlOiIZQl@up5n0LKpnqRLNjPNciLMsI43Baiied5NoM4sHEMygdQZ97ZViWhUfDZa/EBB0cnAzWd4b3vrJJAX8rBpLd8uICko08MnUD7uRl0pyLtgLctHIkkdMpAIBAUhRgniOu4pp5S7WB@lbskHA4HEEKA/Ge/ZIMm48f86Q8 "PHP – Try It Online")
]