g | x | w | all
Bytes Lang Time Link
095Forth gforth250512T204736Zreffu
030Raku Perl 6 rakudo250416T173420Zxrs
063AWK241203T145100Zxrs
096JavaScript Node.js230707T094702ZFhuvi
164Go241203T153548Zbigyihsu
016Uiua SBCS240702T081936Zchunes
180Racket230707T133031ZEd The &
004Thunno 2230525T174116ZThe Thon
051Julia 1.0230324T222622ZAshlin H
8517Nibbles230516T222110ZDLosc
071Haskell230316T192343ZDLosc
056R230510T110754ZKirill L
nanScala230419T014124Z138 Aspe
047Clojure230509T124418ZKirill L
134Pascal FPC230306T145121Zroblogic
077Lua230409T212827Zbluswimm
029Pyth230327T170956Zprincesa
nanAnd any other shell that supports ${var230316T124648Zbxm
023Pip230306T162722ZBaby_Boy
166Java 16+ JDK230316T144732ZFhuvi
087C gcc230304T212851ZErikF
021Raku230310T170520ZSean
062PowerShell Core230307T004246ZJulian
082Excel230306T161051ZEngineer
087Excel ms365230307T121806ZJvdV
029Retina 0.8.2230303T235604ZNeil
019CJam230306T185013Zemirps
358Pascal230306T000013ZKai Burg
110Common Lisp230306T140249Zcoredump
013Japt v2.0a0230304T165327Znoodle p
048Ruby230306T123034ZG B
060Python230304T113207ZThe Thon
008Jelly230305T052630ZUnrelate
037Perl 5 n230304T223607ZKjetil S
135Java JDK230303T214143ZUnmitiga
00605AB1E230304T081622ZThe Thon
032J230304T120240Zovs
068JavaScript230304T042556ZEzioMerc
082JavaScript ES6230303T234356ZArnauld
nan230304T081144ZThe Thon
066jq230304T044020ZGammaFun
035J9.4230304T001140Zsouth
015Charcoal230304T000101ZNeil
034Arturo230303T222340Zchunes
034Mathematica Wolfram Language230303T205211Zdirvine
004Vyxal230303T205940Zmath sca

Forth (gforth), 95 bytes

: f 'Z 'A do 2dup 0 -rot bounds do i c@ toupper j = - loop ?dup if cr i emit ." :". then loop ;

Try it online!

Explanation

Goes through every letter A-Z and counts the occurrences of that letter in the string, then outputs non-zero values alphabetically in the format A:1, B:2, etc.. separated by newlines.

Code Explanation

: f                \ Start word definition
  'Z 'A do         \ Start a counted loop from 65 - 90
    2dup 0 -rot    \ copy string and put 0 to use as the count behind the string on the stack
    bounds do      \ start a counted loop through the string
      i c@ toupper \ get the current character in the string and convert to uppercase
      j = -        \ compare to current letter and subtract result (-1 = true in forth)
    loop           \ end inner counted loop
    ?dup if        \ duplicate result if > 0, start conditional if so
      cr i emit    \ output a newline and the current letter
      ." :".       \ output a colon (:) followed by the result
    then           \ end conditional
  loop             \ end outer loop
;                  \ end word definition 

Raku (Perl 6) (rakudo), 30 bytes

{bag .lc.comb(/<[A..Za..z]>/)}

Attempt This Online!

AWK, 63 bytes

{for($0=tolower($0);j++<26;)if(g=gsub(x=chr(j+96),0))print x,g}

Try it online!

JavaScript (Node.js), 96 116 bytes

-20 bytes using golfing knowledge gained through these last two years

This solution focuses on using ASCII codes to filter non-letter characters, and the use of Object.entries(...) to produce the result

s=>[...(B=Buffer)(s)].map(g=c=>(c=c|32)>96&c<123?g[c=B([c])]=-~g[c]:0)&&Object.entries(g).sort()

Try it online!


Node.js' Buffer helps with converting string to ASCII values both ways, and we store it as the B function.

g is declared as a function but will always be used as an object (it's shorter this way).

c|32 puts letters in lowercase, and we check if it's in the bounds a-z (in ASCII) to determine if it's a letter.

Then we add each letter as properties to the g object if they don't already exist, and increment the corresponding values by one (by using -~g[c] which is equivalent to a +1 with the added benefit of considering undefined as a 0 when each property is declared)

At the end, we output the array of properties of the g object sorted alphabetically.

Go, 164 bytes

import(."strings";u"unicode")
type D=map[rune]int
func f(s string)D{d:=make(D)
for _,r:=range ToLower(s){if u.IsLetter(r){if _,o:=d[r];!o{d[r]=0};d[r]++}}
return d}

Attempt This Online!

Returns a map of runes to ints.

Uiua SBCS, 16 bytes

⊏⊸⍏⊕{⊃⊢⧻}⊸⊛⌵▽⌵⊸±

Try it!

Explanation

⊏⊸⍏⊕{⊃⊢⧻}⊸⊛⌵▽⌵⊸±­⁡​‎⁠⁠⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏‏​⁡⁠⁡‌⁢​‎⁠⁠⁠‎⁡⁠⁣⁤‏‏​⁡⁠⁡‌⁣​‎⁠⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏‏​⁡⁠⁡‌⁤​‎⁠⁠⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏‏​⁡⁠⁡‌­
            ▽⌵⊸±  # ‎⁡remove non-letters
           ⌵      # ‎⁢uppercase
         ⊸⊛       # ‎⁣assign unique index to each unique value
   ⊕{⊃⊢⧻}         # ‎⁤boxed group by letter count
⊏⊸⍏               # ‎⁢⁡sort

Racket -- 180 bytes

#!racket
(let([s hash-set])(foldl(λ(c a)(if(hash-has-key? a c)(s a c(+ 1(hash-ref a c)))(s a c 1)))#hasheq()(map char-downcase(filter char-alphabetic?(string->list(read-line))))))

Try it online!


Explanation

This program returns a hash table of key-value pairs. The keys in the hash table are the characters and the values are the number of occurences in the string.

Characters are represented by a #\ prefix. So the string "Apple" can be thought of as:

(display "Apple")
(display (string #\A #\p #\p #\l #\e))

The pseudo-code of my answer (algorithm?) is:

  1. Read user input.
  2. Filter only the alphabetic characters.
  3. Convert all left-over characters into lowercase.
  4. Iterate left-to-right using a hash table.
    1. If the hash table has the character as a key, increment the key's value.
    2. Otherwise, add a new key representing the character and set the value to 1.
  5. Return the resulting hash table.
#lang racket

(foldl (lambda (char acc)
         (if (hash-has-key? acc char)
             (hash-set acc char (add1 (hash-ref acc char)))
             (hash-set acc char 1)))
       #hasheq()
       (map char-downcase
            (filter char-alphabetic?
                    (string->list (read-line)))))

Thunno 2, 4 bytes

ỊLƈṠ

Attempt This Online!

Explanation

ỊLƈṠ  # Implicit input
Ị     # Remove non-alphabets
 L    # Lowercase
  ƈ   # Counts
   Ṡ  # Sort
      # Implicit output

Julia 1.0, 51 bytes

~s=(x='A':'Z';y=@.sum(in(x=>x+32),s);(x.=>y)[y.>0])

Try it online!

54 byte solution

~s=(y='A':'Z'.|>x->x=>sum(in(x=>x+32),s))[@.last(y)>0]

Try it online!

55 byte solution

~s=[x=>y for x='A':'Z' for y=sum(in(x=>x+32),s) if y>0]

Try it online!

Nibbles, 8.5 bytes (17 nibbles)

=~|.@`($\$a$~/$$,

Attempt This Online!

I could save 0.5 bytes by returning (count, letter) pairs instead of (letter, count).

Explanation

There's probably a better way to do this.

=~|.@`($\$a$~/$$,$
    @               First line of stdin
   .                Map to each character:
     `($             Lowercase
  |                 Filter by this function:
        \$a          Is the character a letter?
=~                  Group and sort by:
           $         Each letter
                    We now have the lowercase letters from the input, grouped
                    by letter (giving a list of strings) and sorted
                    For each string of identical letters (implicit):
            ~        Return a tuple of:
             /$$      The first character of the string
                ,$    The length of the string

The / ... $ idiom used in /$$ means "right-fold on the identity function," which yields the leftmost element of a list.

Haskell, 75 71 bytes

-4 bytes thanks to Lynn

f s=[(l,n)|(u,l)<-zip['A'..'Z']['a'..],n<-[sum[1|c<-s,l==c||u==c]],n>0]

Attempt This Online!

Explanation

List comprehensions seem to be shorter than map and filter.

f s=

Define our function, f, which takes a string s.

zip['A'..'Z']['a'..]

Create a list of (uppercase letter, lowercase letter) tuples.

(u,l)<-___

For each uppercase letter u and lowercase letter l in that list...

,n<-sum[1|c<-s,l==c||u==c]

Let n be the number of characters c in s for which c equals one of l and u...

[(l,n)|____,n>0]

Return a list of tuples of l and the corresponding n where n is greater than 0.


A solution using toLower from Data.Char comes in at 76 bytes:

import Data.Char
f s=[(l,n)|l<-['a'..'z'],n<-[sum[1|c<-s,l==toLower c]],n>0]

R, 56 bytes

\(s,a=el(strsplit(tolower(s),"")))table(a[a%in%letters])

Attempt This Online!

Scala, 112 108 bytes

Saved 4 bytes thanks to the comment of @Kirill L.


Golfed version. Try it online!

def f(s:String)=s.toUpperCase.filter(c=>c>='A'&&c<='Z').groupBy(x=>x).mapValues(_.length).toSeq.sortBy(_._1)

Ungolfed version. Try it online!

import scala.collection.mutable

object Main {
  def main(args: Array[String]): Unit = {
    println(frequency("hello world"))
    println(frequency("The quick brown fox jumps over the lazy dog"))
  }

  def frequency(s: String): Seq[(Char, Int)] = {
    val uppercaseChars = s.toUpperCase
    val counts = mutable.Map.empty[Char, Int].withDefaultValue(0)

    uppercaseChars.foreach(c => {
      if (c >= 'A' && c <= 'Z') {
        counts(c) += 1
      }
    })

    counts.toSeq.sortBy(_._1)
  }
}

Clojure, 47 bytes

#(frequencies(re-seq #"[a-z]"(.toLowerCase %)))

Try it online!

Returns a Map as allowed by OP in the comments. To obtain a sorted colllection this should be additionally wrapped with sort.

Pascal (FPC), 185 ... 134 bytes

var c,d:char;s:string;i:word;begin;read(s);for c:=^to^do begin;i:=0;for d in upcase(s)do i+=ord(c=d);if i>0then writeln(c,i)end;end.

Try it online! (134b, courtesy of @michael-tkach)

 150b  155b  160b  161b  162b  185b

Iterates over the alphabet and the input string s, comparing chars d and c. The for <char> in <string> syntax was handy. Implemented some of the Pascal golfing tips.

Lua, 77 bytes

s=...for i=65,90 do c=s.char(i)_,n=s:upper():gsub(c,c)f=n>0 and print(c,n)end

Try it online!

s=...                        -- Take argument as input
for i=65,90 do
    c=s.char(i)              -- Get the char represented by charcode i
    _,n=s:upper():gsub(c,c)  -- n stores the number of occurrences of char c
    f=n>0 and print(c,n)     -- if n>0, print char/count pair
end

Pyth, 29 bytes

FNS{rzZI&<"`"N <N"{"(N /rzZN)

Try it online!

Explanation

Used a for loop F to iterate over the letters which are valid by I&<"`"N <N"{" which checks if a letter in the string is within backtick and open curly bracket.

rzZ - converts the input string in lower i.e. input().lower() in Python.

S{ - used to sort the string.

And any other shell that supports ${var,,} to downcase.

Bash, 35 bytes

grep -o [a-z]<<<${*,,}|sort|uniq -c

Try it online!

Pip, 21 23 bytes

YSSLCaFiUQy@XL;Pi.(iNy)

How?

YSSLCaFiUQy@XL;Pi.(iNy)  : One arg; str
     a                   : First input
   LC                    : To lowercase
 SS                      : Sort
Y                        : Yank
      Fi                 : For i in
        UQy              : Unique values of y
           @XL           : Only lowercase alphabet characters
               P           : Print
                i          : Letter
                 .         : Concatenated with
                   iN      : Letter in
                     y     : Input

Try It Online!

Java 16+ (JDK), 166 bytes

Tried to use only inline stream as a different approach, but couldn't beat the best Java answer.

s->s.toLowerCase().chars().distinct().filter(e->e>96&e<123).sorted().mapToObj(e->new String[]{(char)e+"",s.toLowerCase().chars().filter(o->o==e).count()+""}).toList()

JDoodle   (because TIO currently only supports Java up to v12)

C (gcc), 97 94 87 bytes

Uses uppercase as the challenge only states case-insensitive.

f(char*s){for(int a[96]={},i=64;i<90;)*s?a[*s++&95]++:a[++i]&&printf("%c:%d ",i,a[i]);}

Try it online!

Ungolfed:

f(char*s){
  for(int a[96]={},i=64;
      i<90;) // for all characters in string, then from 'A' to 'Z':
    *s? // done processing string?
      a[*s++&95]++: // no, uppercase letter and increment count
      a[++i]&&printf("%c:%d ",i,a[i]); // yes, print counts
}

Raku, 21 bytes

*.lc.comb(/<:L>/).Bag

Try it online!

PowerShell Core, 62 bytes

$args-match'[a-z]'|%{"$_"|% *l*r}|group|%{,($_.Name,$_.Count)}

Try it online!

$args-match'[a-z]'        # For each character argument, exclude non letters characters
|%{"$_"|% *l*r}           # Converts to lowercase
|group                    # Groups them
|%{,($_.Name,$_.Count)}   # build pairs of letters and counts

Excel, 85 82 bytes

Saved 3 bytes thanks to JvdV

=LET(c,CHAR(ROW(65:90)),q,LEN(A1)-LEN(SUBSTITUTE(UPPER(A1),c,)),FILTER(c&"="&q,q))

The LET() function allows you to assign variables to values and reference them by name later. Every term is a pair except the last which is the output.

Screenshot

Excel (ms365), 87 bytes

enter image description here

Formula in B1:

=LET(x,CHAR(ROW(97:122)),y,LEN(A1)-LEN(SUBSTITUTE(LOWER(A1),x,)),FILTER(HSTACK(x,y),y))

And 83 bytes if you don't want tupples, but a concatenation would do:

=LET(x,CHAR(ROW(97:122)),y,LEN(A1)-LEN(SUBSTITUTE(LOWER(A1),x,)),FILTER(x&"-"&y,y))

Retina 0.8.2, 38 29 bytes

T`Llp`ll_
O`.
(.)\1*
$1, $.&¶

Try it online! Output includes trailing newlines. Saved 9 bytes thank to @FryAmTheEggman. Explanation:

T`Llp`ll_

Convert letters to lower case and delete all other printable ASCII.

O`.

Sort the characters.

(.)\1*
$1, $.&¶

Output the count of each letter.

Previous 38-byte version worked with any input, not just printable ASCII:

T`L`l
O`.
M!`([a-z])\1*
(.)\1*
$1, $.&

Try it online! Explanation:

T`L`l

Lowercase the input string.

O`.

Sort the characters.

M!`([a-z])\1*

Extract the runs of identical letters.

(.)\1*
$1, $.&

Output the count of each letter.

31 30 bytes in Retina 1:

.
$L
O`.
L$`([a-z])\1*
$1, $.&

Try it online! Explanation:

.
$L

Lowercase the input.

O`.

Sort the characters.

L$`([a-z])\1*

For each run of identical letters...

$1, $.&

... output the count of each letter.

CJam, 19 bytes

el_'{,97>--$e`{W%}%

Try it online!

Returns strings in the form of {char}{count}

Explanation

el_'{,97>--$e`{W%}% # a function taking an input string
el_      --         # remove all elements of the input which do not occur in
   '{,97>           # the character range lowercase a-z
           $        # sort
            e`      # run-length encode (1h1e2l as an example)
              {W%}% # to meet the output specification of {char}{count} and not {count}{char}
                    # we reverse each of the arrays in this sorted array

Pascal, 590 358 bytes

This full program requires a processor supporting features of ISO standard 10206 “Extended Pascal”. Furthermore,

The GPC (GNU Pascal Compiler) fulfills these conditions on an x86‑64 Linux system if compiling with the ‑‑extended-pascal option.

program p(input,output);var c:char;f:array[char]of integer value[otherwise 0];begin while not EOF do begin read(c);c:=(c+'ABCDEFGHIJKLMNOPQRSTUVWXYZ')[index('abcdefghijklmnopqrstuvwxyz',c)+1];f[c]:=f[c]+1 end;for c in['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']do if 0<f[c]then writeLn(c,f[c])end.

Ungolfed:

program letterFrequency(input, output);
    var
        c: char;
        letterFrequency: array[char] of integer value [otherwise 0];
    begin
        while not EOF(input) do
        begin
            { `read(input, c)` is equivalent to `c := input^; get(input)`. }
            read(input, c);
            
            { There is no built-in “change case” function as Pascal does not
              guarantee the coexistence of lower and uppercase letters. The EP
              function `index(source, pattern)` returns the index of the first
              occurence of `pattern` in `source`. If `pattern` is not found,
              zero is returned. Remember, `string` indices are 1-based, thus
              add `1` to select the prepended value of `c`. }
            c := (c + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
                [index('abcdefghijklmnopqrstuvwxyz', c) + 1];
            
            letterFrequency[c] := letterFrequency[c] + 1;
        end;
        
        for c in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] do
        begin
            if letterFrequency[c] > 0 then
            begin
                writeLn(c, letterFrequency[c]);
            end;
        end;
    end.

See also FreePascal implementation.

Common Lisp, 110 bytes

(lambda(s)(reduce(lambda(s c &aux(e(assoc c s)))(if e(prog1 s(incf(cdr e)))(acons c 1 s)))s :initial-value()))

The ungolfed version is:

(defun frequencies (string)
  (reduce (lambda (map char)
            (let ((entry (assoc char map)))
              (if entry
                  (prog1 map
                    (incf (cdr entry)))
                  (acons char 1 map))))
          string
          :initial-value nil))

For example:

> (frequencies "abcbad")
((#\d . 1) (#\c . 1) (#\b . 2) (#\a . 2))

Japt v2.0a0, 15 13 bytes

v r\L ü ËÎ+DÊ

Try it

-2 by Shaggy

Outputs a list of “ab” strings where a is the character and b is the number of occurrences

Japt, 7 bytes

ü ËÎ+DÊ

Try it

Doesn’t remove non-letters

Made on My iPhone

ü ËÎ+DÊ # input string
ü       # list of same characters grouped together
  Ë     # map each string of same char to:
   Î    #   the character
    +DÊ #   append length of group

Ruby, 48 bytes

->s{[*s.upcase.chars.tally.slice(*?A..?Z).sort]}

Attempt This Online!

Python, 60 bytes

lambda s:{c:s.lower().count(c)for c in s.lower()if'`'<c<'{'}

Attempt This Online!

Returns a dictionary. Suggested by @JonathanAllan.

Python, 70 bytes

lambda s:sorted((c,s.lower().count(c))for c in{*s.lower()}if'`'<c<'{')

Attempt This Online!

Returns a sorted list of tuples.

Python, 75 bytes

lambda s:sorted({*zip(x:=[*filter(str.isalpha,s.lower())],map(x.count,x))})

Attempt This Online!

Returns a sorted list of tuples.

Commented

lambda s: sorted(          # Define an anonymous function which takes a string, s,
                           # and returns a sorted version of the following:
  (c, s.lower().count(c))  #  a tuple of the character, c, and its count
                           #  in the lowercase version of s
  for c in {*s.lower()}    #  for each character, c, in the lowercase version of s uniquified
  if '`' < c < '{' )       #  but only keep it if it is between the characters
                           #  '`' (right before 'a') and '{' (right after 'z')
                           #  i.e., it's alphabetic
lambda s: sorted(          # Define an anonymous function which takes a string, s,
                           # and returns a sorted version of the following:
  {*zip(                   #  a uniquified version of two iterables zipped together:
    x := [*filter(         #   filter by
      str.isalpha,         #   is the character in the alphabet
      s.lower()            #   apply to a lowercase version of s
    )],                    #   and store in x
    map(x.count, x) )})    #   the counts of each character in x

Jelly, 8 bytes

ŒlḟŒuṢŒr

Try it online!

At least slightly less boring than if I'd used Øa. Feels ever so vaguely beatable, still...

Œl          The lowercased input,
  ḟŒu       with elements of the uppercased input filtered out.
     ṢŒr    Sort and run-length encode.

Perl 5 -n, 37 bytes

for$l(a..z){/$l/i&&say"$l ".s/$l//gi}

Try it online!

Java (JDK), 151 142 135 bytes

s->{var m=new java.util.TreeMap();for(var c:s.replaceAll("[^a-zA-Z]","").toCharArray())m.merge(c|=32,1,(a,b)->(int)a+(int)b);return m;}

Try it online!

Saved 15 bytes thanks to Kevin Cruijssen.

05AB1E, 9 6 bytes

álТøê

Try it online!

Input as a list of characters.

Alternative 6 bytes answer with input as a string: (suggested by @KevinCruijssen)

ál{Åγø

Try it online!

-3 thanks to @KevinCruijssen

Explanation

á       # Filter for alphabetic characters only
 lÐ     # Convert to lowercase and triplicate
   ¢    # Count the occurences of each character
    øê  # Zip, sort, and uniquify
á       # Filter for alphabetic characters only
 l{     # Convert to lowercase and sort
   Åγ   # Run-length encode the string
     ø  # Zip

J, 32 bytes

(u:97+i.26)(e.#[;"+1#.=/)tolower

Attempt This Online!

u:97+i.26 A character vector of all lowercase letters.
tolower Input converted to lowercase.
(u:97+i.26)(...)tolower Apply inner function with lowercase alphabet as left argument and lowercased string as right argument.
=/ Equality table.
1#. Sum each row. This gives a vector of length 26 with the count of each letter in the input.
[;"+ Pair up each letter with its count.
e.# Only keep the pairs where the letter is an element of the input.

JavaScript, 68 bytes

s=>s.toLowerCase(m={}).match(/[a-z]/g).sort().map(l=>m[l]=-~m[l])&&m

Try it:

f=s=>s.toLowerCase(m={}).match(/[a-z]/g).sort().map(l=>m[l]=-~m[l])&&m

;[
  'hello world',
  'The tuick brown fox jumps over the lazy dog'
].forEach(s=>console.log(f(s)))

UPD 73 -> 68

Thanks to Shaggy for the tips (one, two) to reduce bytes count

JavaScript (ES6), 82 bytes

Probably takes a list of pairs a bit too literally... ¯\_(ツ)_/¯

s=>[...s.toLowerCase()].sort().join``.match(/([a-z])\1*/g).map(s=>[s[0],s.length])

Try it online!

Thunno, \$18\log_{256}(96)\approx\$ 14.82 bytes

UgAzsAqkztcsZZZUz:

Attempt This Online!

Explanation

U           # Lowercase
 gAzsAqk    # Alphabetic characters only
        zt  # Triplicate
cs          # Counts of each
  ZZ        # Zip
    ZU      # Uniquify
      z:    # Sort

jq, 66 bytes

ascii_downcase|reduce match("[a-z]";"g").string as $c({};.[$c]+=1)

Try it online!

reduce is just too good.

J9.4, 35 bytes

'\pL'(~.,:<@#/.~)@/:~@rxall tolower

J903, 37 bytes

'[a-z]'(~.,:<@#/.~)@/:~@rxall tolower

Attempt This Online!

Explanation is the same, but the PCRE engine was updated which makes the regex 2 bytes shorter.

'\pL'(~.,:<@#/.~)@/:~@rxall tolower
                            tolower  NB. lowercase the input
'\pL'                 rxall          NB. match every letter
                  /:~@               NB. then sort
     (          )@                   NB. then invoke a monadic fork
            #/.~                     NB. count the occurrences of each letter
          <@                         NB. then box each result
      ~.                             NB. uniquify the matches
        ,:                           NB. stack matches on top of counts

Charcoal, 15 bytes

⭆¹ΦEβ⟦λ№↧θλ⟧§λ¹

Try it online! Link is to verbose version of code. Explanation:

    β           Predefined variable lowercase alphabet
   E            Map over letters
      λ         Current letter
     ⟦     ⟧    Make into tuple with
       №        Count of
          λ     Current letter in
         θ      Input string
        ↧       Lowercased
  Φ             Filtered where
            §λ¹ Count was nonzero
⭆¹              Pretty-print

Arturo, 34 bytes

$=>[match lower&{/\pL}|tally|sort]

Try it

Mathematica (Wolfram Language), 34 bytes

KeySort@*LetterCounts@*ToLowerCase

TIO.run is in the comments below. Edited to save 4 bytes thanks to a suggestion from ZaMoC.

Vyxal, 4 bytes

ǍɽĊs

Try it Online!

How it works:

ǍɽĊs
Ǎɽ    Remove non-alphabetical chars and lowercase
  Ċ   Count of each element
   s  Sorted