11
\$\begingroup\$

Write a program or function that takes in a string and outputs a count of each modern English alphabet letter in the string, case-insensitive.

Input: A string consisting of printable ASCII characters (code points 32-126).

Output: A list of pairs, where each pair consists of a letter and its count in the string. The list should be sorted in alphabetical order by letter. The list shall omit letters occurring zero times.

Test Cases

Input: "hello world"
Output: [('d', 1), ('e', 1), ('h', 1), ('l', 3), ('o', 2), ('r', 1), ('w', 1)]

Input: "The quick brown fox jumps over the lazy dog"
Output: [('a', 1), ('b', 1), ('c', 1), ('d', 1), ('e', 3), ('f', 1), ('g', 1), ('h', 2), ('i', 1), ('j', 1), ('k', 1), ('l', 1), ('m', 1), ('n', 1), ('o', 4), ('p', 1), ('q', 1), ('r', 2), ('s', 1), ('t', 2), ('u', 2), ('v', 1), ('w', 1), ('x', 1), ('y', 1), ('z', 1)]

Code Golf Specifics:

Your goal is to write the shortest possible code in bytes that correctly solves the problem. Your code must be able to handle any valid input within the given constraints. Note that your code should treat uppercase and lowercase letters as equivalent.

Good luck!

\$\endgroup\$
15
  • 3
    \$\begingroup\$ I suggest test cases involving more non-letters than just spaces. \$\endgroup\$
    – chunes
    Commented Mar 3, 2023 at 20:42
  • 7
    \$\begingroup\$ 90% sure this is a dupe. \$\endgroup\$
    – Shaggy
    Commented Mar 3, 2023 at 21:15
  • 4
    \$\begingroup\$ Is outputting a Map acceptable? \$\endgroup\$ Commented Mar 3, 2023 at 21:27
  • 4
    \$\begingroup\$ closely related \$\endgroup\$
    – math scat
    Commented Mar 4, 2023 at 7:44
  • 8
    \$\begingroup\$ Treating uppercase and lower case letters as equivalent is IMO a signficant difference from the other challenge. I don't like either challenge personally, but the dupe target is pretty dated, with rules and format I wouldn't expect to see today and I'd prefer if that were closed as a dupe of this. \$\endgroup\$
    – Wheat Wizard
    Commented Mar 4, 2023 at 12:19

41 Answers 41

6
\$\begingroup\$

Vyxal, 4 bytes

ǍɽĊs

Try it Online!

How it works:

ǍɽĊs
Ǎɽ    Remove non-alphabetical chars and lowercase
  Ċ   Count of each element
   s  Sorted 
\$\endgroup\$
6
\$\begingroup\$

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
\$\endgroup\$
4
  • \$\begingroup\$ OP appears to allow a dictionary as output in the comments, which would make this only \$60\$ with lambda s:{c:s.lower().count(c)for c in s.lower()if'`'<c<'{'}. \$\endgroup\$ Commented Mar 4, 2023 at 18:29
  • \$\begingroup\$ @JonathanAllan that wouldn't be sorted though, would it? Adding the sort would be 76 bytes \$\endgroup\$
    – The Thonnu
    Commented Mar 4, 2023 at 20:08
  • \$\begingroup\$ No, it wouldn't be sorted, but if they allow what was linked to then a plain dictionary is acceptable. \$\endgroup\$ Commented Mar 4, 2023 at 22:23
  • \$\begingroup\$ @JonathanAllan alright, thanks. \$\endgroup\$
    – The Thonnu
    Commented Mar 5, 2023 at 10:03
5
\$\begingroup\$

jq, 66 bytes

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

Try it online!

reduce is just too good.

\$\endgroup\$
5
\$\begingroup\$

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.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ You can remove <Character,Long> and use 1,(a,b)->(int)a+(int)b instead for -7 bytes. And you can also changed the .chars().forEach(c-> with (char)c to a regular for(var c:s....toCharArray()) and c for another -2 bytes: try it online. \$\endgroup\$ Commented Mar 4, 2023 at 15:32
  • 1
    \$\begingroup\$ Oh, and -6 more bytes by using c|=32 instead of c, adding the case-insensitive flag (?i) to the regex, and removing the .toLowerCase(): try it online. :) (Relevant tip.) \$\endgroup\$ Commented Mar 4, 2023 at 15:38
  • 1
    \$\begingroup\$ @KevinCruijssen Nice, I'd forgotten about those bitwise tricks with ASCII. Actually it seems [^a-zA-Z] is shorter than (?i)[^a-z] though. \$\endgroup\$ Commented Mar 4, 2023 at 15:40
5
\$\begingroup\$

Perl 5 -n, 37 bytes

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

Try it online!

\$\endgroup\$
5
\$\begingroup\$

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.

  • c,CHAR(ROW(65:90)) creates an array of the strings A through Z.
  • q,LEN(a)-LEN(SUBSTITUTE(UPPER(a),c,)) creates an array of how many times each letter appears by measuring the length of the input vs. the length if we replace that letter with nothing. SUBSTITUTE() is case-sensitive so we transform the input to uppercase.
  • FILTER(c&"="&q,q) combines the array of letters with the count of each character and then filter for just those where the count (q) was truthy (anything not zero).

Screenshot

\$\endgroup\$
2
  • 1
    \$\begingroup\$ If OP doesn't require tupples and uppercase answers are allowed, then maybe remove the 1st variable and nest it inside SUBSTITUTE(): =LET(x,CHAR(ROW(65:90)),y,LEN(A1)-LEN(SUBSTITUTE(UPPER(A1),x,)),FILTER(x&"="&y,y)) for 82 bytes. \$\endgroup\$
    – JvdV
    Commented Mar 7, 2023 at 12:27
  • 1
    \$\begingroup\$ @JvdV Good point. I usually write LET() functions for PPCG in their long form and then cut it down. I overlooked the fact that LEN() doesn't care about case so I wasn't saving bytes*2 with the variable. \$\endgroup\$ Commented Mar 7, 2023 at 14:37
5
\$\begingroup\$

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
\$\endgroup\$
2
  • \$\begingroup\$ Love the solution, but it's missing a sort, unfortunately. \$\endgroup\$ Commented Mar 8, 2023 at 4:04
  • \$\begingroup\$ @BrunoBrant The output seems sorted by characters already, it looks like group does it by default. Do you have a failing test case that I could try? \$\endgroup\$
    – Julian
    Commented Mar 8, 2023 at 18:58
5
\$\begingroup\$

C (gcc), 97 94 87 bytes

  • -3 thanks to Unrelated String
  • -7 thanks to c--

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
}
\$\endgroup\$
2
  • \$\begingroup\$ a[255] should be a[256]? \$\endgroup\$
    – Oskar Skog
    Commented Mar 4, 2023 at 23:59
  • \$\begingroup\$ If uppercase is allowed, 95 \$\endgroup\$ Commented Mar 5, 2023 at 5:23
5
\$\begingroup\$

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.

\$\endgroup\$
1
4
\$\begingroup\$

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

\$\endgroup\$
4
  • 4
    \$\begingroup\$ (m[l]||0)+1 can be -~m[l] \$\endgroup\$
    – Shaggy
    Commented Mar 4, 2023 at 7:51
  • 4
    \$\begingroup\$ Oh, and you can initialise m within toLowerCase() to save another byte. \$\endgroup\$
    – Shaggy
    Commented Mar 4, 2023 at 8:07
  • 1
    \$\begingroup\$ Actually, assigning the RegEx to m rather than using an explicit object should work, too. \$\endgroup\$
    – Shaggy
    Commented Mar 4, 2023 at 13:09
  • \$\begingroup\$ @Shaggy You mean m=/[a-z]/g? \$\endgroup\$
    – EzioMercer
    Commented Mar 4, 2023 at 13:23
4
\$\begingroup\$

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
\$\endgroup\$
2
  • \$\begingroup\$ ʒa} is the builtin á. :) Also, you can remove the S by taking the input as a list of characters: try it online. Or if you prefer to keep the input as string, here is a 6 bytes alternative using the run-length encode builtin: ál{Åγø \$\endgroup\$ Commented Mar 4, 2023 at 15:24
  • \$\begingroup\$ @KevinCruijssen thanks, updated! \$\endgroup\$
    – The Thonnu
    Commented Mar 4, 2023 at 15:36
4
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ It seems you can save a bit by removing the bad characters instead of keeping the good ones. \$\endgroup\$ Commented Mar 7, 2023 at 4:23
  • \$\begingroup\$ Perhaps a few more by using T more. \$\endgroup\$ Commented Mar 7, 2023 at 4:25
4
\$\begingroup\$

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!

\$\endgroup\$
2
  • \$\begingroup\$ This works on the current test cases, but the challenge specifies that all non-letter characters should be ignored, not just spaces. I think looping over z and adding some logic to skip letters whose count is zero will be your best bet. \$\endgroup\$
    – DLosc
    Commented Mar 16, 2023 at 18:23
  • \$\begingroup\$ @DLosc Sorry about that! This should work? Lmk if there're any more problems :) \$\endgroup\$
    – Baby_Boy
    Commented Mar 16, 2023 at 19:38
3
\$\begingroup\$

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.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ 34 bytes here you are... ;) \$\endgroup\$
    – ZaMoC
    Commented Mar 3, 2023 at 21:00
  • \$\begingroup\$ I just got it to work on TIO, but your answer is shorter. Thanks. \$\endgroup\$
    – dirvine
    Commented Mar 3, 2023 at 21:05
  • 1
    \$\begingroup\$ Hi, in order to avoid [] you can also use KeySort@LetterCounts@ToLowerCase@#& for 35 bytes. But in this case you can "chain" 3 functions using @* for 1 extra byte. \$\endgroup\$
    – ZaMoC
    Commented Mar 3, 2023 at 21:10
  • \$\begingroup\$ Yeah, I should have thought about the operator form. Thanks for your suggestion. I did that on my answer to this related question: codegolf.stackexchange.com/questions/258558/… \$\endgroup\$
    – dirvine
    Commented Mar 3, 2023 at 21:13
  • \$\begingroup\$ It's strange that LetterCounts works fine on tio.run, but WordCounts causes an error. (WordCounts is used in the answer to the question in the previous comment.) I guess WordCounts uses some NLP, but that isn't needed for just counting letters. \$\endgroup\$
    – dirvine
    Commented Mar 3, 2023 at 21:17
3
\$\begingroup\$

Arturo, 34 bytes

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

Try it

\$\endgroup\$
3
\$\begingroup\$

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
\$\endgroup\$
3
\$\begingroup\$

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
\$\endgroup\$
3
\$\begingroup\$

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
\$\endgroup\$
3
\$\begingroup\$

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!

\$\endgroup\$
4
  • \$\begingroup\$ s.split(c) is not right. You take into account the case of letters when separating. Because of this for The tuick brown fox jumps over the lazy dog your function outputs only 2 t instead of 3 t \$\endgroup\$
    – EzioMercer
    Commented Mar 4, 2023 at 4:15
  • \$\begingroup\$ @EzioMercer You're right. Updated to a new method. \$\endgroup\$
    – Arnauld
    Commented Mar 4, 2023 at 8:55
  • \$\begingroup\$ 77 bytes \$\endgroup\$
    – Shaggy
    Commented Mar 4, 2023 at 13:13
  • \$\begingroup\$ @Shaggy I decided to return a list and there's already an answer using an object, so I'll stick to that. \$\endgroup\$
    – Arnauld
    Commented Mar 4, 2023 at 15:20
3
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

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.
\$\endgroup\$
3
\$\begingroup\$

Ruby, 48 bytes

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

Attempt This Online!

\$\endgroup\$
1
  • \$\begingroup\$ No need to use outer splat. ->s{s.upcase.chars.tally.slice(*?A..?Z).sort} works just fine. \$\endgroup\$
    – radarek
    Commented May 16, 2023 at 9:09
3
\$\begingroup\$

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
\$\endgroup\$
6
  • \$\begingroup\$ Note that the challenge requires the counting of letters only and that the output be sorted by character. \$\endgroup\$
    – Shaggy
    Commented Mar 6, 2023 at 10:14
  • \$\begingroup\$ @Shaggy Oops, forgot to read the rules lol \$\endgroup\$ Commented Mar 6, 2023 at 12:07
  • 1
    \$\begingroup\$ 13 bytes in v2. \$\endgroup\$
    – Shaggy
    Commented Mar 6, 2023 at 12:32
  • \$\begingroup\$ @Shaggy Only took you 20 seconds to golf mine! Thanks \$\endgroup\$ Commented Mar 6, 2023 at 12:36
  • \$\begingroup\$ You might be able to pass the v as an argument to ü instead to save a byte but we'll need clarification on whether the output must be lowercase or if it can be mixed case. \$\endgroup\$
    – Shaggy
    Commented Mar 6, 2023 at 12:44
3
\$\begingroup\$

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))
\$\endgroup\$
3
\$\begingroup\$

Pascal, 590 358 bytes

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

  • the implementation-dependent behavior of the selection order in a for … in set member iteration statement must be ascending,
  • the implementation-defined default TotalWidth for integer-type write arguments must be sufficiently large in order to leave some space between a character and the number, and
  • the implementation-defined set of available characters contains both upper and lowercase characters.

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.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ “Pascal does not guarantee that the characters 'a' through 'z' are arranged in contiguous order.” — Ok, Pascal does not, but what about you? q:=Pos(c,'abcdefghijklmnopqrstuvwxyz');if q>0then c:='ABCDEFGHIJKLMNOPQRSTUVWXYZ'[q]; \$\endgroup\$
    – manatwork
    Commented Mar 6, 2023 at 4:18
  • \$\begingroup\$ @manAtWork Thanks for essentially reminding me of this golfing tip. I have adapted it to Extended Pascal, that means using the standardized index. \$\endgroup\$ Commented Mar 6, 2023 at 18:36
3
\$\begingroup\$

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
\$\endgroup\$
3
\$\begingroup\$

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]
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Slightly shorter is f s=[(l,n)|(u,l)<-zip['A'..'Z']['a'..],n<-[sum[1|c<-s,c==u||c==l]],n>0] \$\endgroup\$
    – lynn
    Commented May 12, 2023 at 14:33
3
\$\begingroup\$

Julia 1.0, 51 bytes

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

Try it online!

  • Thanks to MarcMush for the following:
    1. replace sum(==(x),uppercase(s)) with sum(in(x=>x+32),s)
    2. replace Tuples with Pairs (e.g., (a,b) vs. a=>b).

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!

\$\endgroup\$
6
  • 1
    \$\begingroup\$ Thanks! Actually, I was including the assignment in the header, since I've seen other examples of that. I've now moved it to back to the code and updated the byte count. \$\endgroup\$ Commented Mar 27, 2023 at 13:43
  • 1
    \$\begingroup\$ This would be fine, since it is an anonymous function, and the header is just to store it, but what you did was a snippet, not a function (not valid Julia code by itself). If other answers are not valid, I try to point it out (not many people check Julia answers) \$\endgroup\$
    – MarcMush
    Commented Mar 28, 2023 at 15:43
  • 1
    \$\begingroup\$ It is probably golfable further, but this works for 62 bytes \$\endgroup\$
    – Kirill L.
    Commented May 10, 2023 at 10:33
  • 1
    \$\begingroup\$ -6 bytes by avoiding the way too long uppercase and using Pairs instead of Tuples Try it online! \$\endgroup\$
    – MarcMush
    Commented May 18, 2023 at 19:07
  • 1
    \$\begingroup\$ @MarcMush Brilliant! I can improve several of my other posts with those suggestions. I used your ideas to trim 11 bytes off my 62 byte solution, so now it's the best so far. \$\endgroup\$ Commented May 24, 2023 at 23:11
2
\$\begingroup\$

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))
\$\endgroup\$
2
\$\begingroup\$

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)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.