15
\$\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!

Kai Burghardt
1,2042 gold badges11 silver badges15 bronze badges
asked Mar 3, 2023 at 20:30
\$\endgroup\$
13
  • 4
    \$\begingroup\$ I suggest test cases involving more non-letters than just spaces. \$\endgroup\$ Commented Mar 3, 2023 at 20:42
  • 7
    \$\begingroup\$ 90% sure this is a dupe. \$\endgroup\$ Commented Mar 3, 2023 at 21:15
  • 4
    \$\begingroup\$ Is outputting a Map acceptable? \$\endgroup\$ Commented Mar 3, 2023 at 21:27
  • 5
    \$\begingroup\$ closely related \$\endgroup\$ 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\$ Commented Mar 4, 2023 at 12:19

47 Answers 47

1
2
2
\$\begingroup\$

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)
 }
}
answered Apr 19, 2023 at 1:41
\$\endgroup\$
1
  • \$\begingroup\$ I don't know Scala, but I think identity can be replaced by x=>x \$\endgroup\$ Commented May 9, 2023 at 14:24
2
\$\begingroup\$

Thunno 2, 4 bytes

ỊLƈṠ

Attempt This Online!

Explanation

ỊLƈṠ # Implicit input
Ị # Remove non-alphabets
 L # Lowercase
 ƈ # Counts
 Ṡ # Sort
 # Implicit output
answered May 25, 2023 at 17:41
\$\endgroup\$
2
\$\begingroup\$

Java (JDK), 124 (削除) 166 (削除ここまで) bytes

-42 bytes by changing the algorithm, types, and doing all the filtering, conversion, and sorting directly in the collect

Tried to use only inline stream and ASCII codes as a different approach, (削除) but couldn't beat the best Java answer (削除ここまで) and this time it did beat the best Java answer!

Output is a Map<Character, Integer>

s->s.chars().collect(java.util.TreeMap::new,(m,k)->{if((k|=32)>96&k<123)m.merge((char)k,1,(a,b)->(int)a+(int)b);},(x,y)->{})

Try it online!


A little explanation : .chars() starts a Stream with the ASCII codes of the String

.collect() acts exactly like the SQL GROUP BY we want, when provided with the right arguments.

TreeMap provides the output format we need, with the added benefit of naturally sorting the entries!

k|=32 converts uppercase ASCII codes into lowercase ASCII codes.

k>96&k<123 filters non-letter ASCII codes.

(char)k converts back the ASCII codes into the corresponding characters.

Unfortunately, even though the Stream itself is an IntStream, this information is lost when using the non-parameterized Map, so we have to manually cast to (int) when updating the count of a given character.

(x,y)->{} seems to be the shortest way to provide the surprinsingly mandatory last parameter in the collect() (used for potential parallel Streams, that isn't the case here).

answered Mar 16, 2023 at 14:47
\$\endgroup\$
1
\$\begingroup\$

Raku, 21 bytes

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

Try it online!

  • .lc converts the input string to lowercase.
  • .comb(/<:L>/) produces a list of all non-overlapping matches for the regex <:L> (a letter) in that string.
  • .Bag converts that list to a Bag, a set with multiplicity, which is also an associative mapping and thus allowed as a return value per comments added to the challenge.
answered Mar 10, 2023 at 17:05
\$\endgroup\$
1
\$\begingroup\$

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.

answered Mar 27, 2023 at 17:09
\$\endgroup\$
1
\$\begingroup\$

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
answered Apr 9, 2023 at 21:28
\$\endgroup\$
1
\$\begingroup\$

R, 56 bytes

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

Attempt This Online!

answered May 10, 2023 at 11:07
\$\endgroup\$
1
\$\begingroup\$

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.

answered May 16, 2023 at 22:21
\$\endgroup\$
1
\$\begingroup\$

Uiua SBCS , 16 bytes

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

Try it!

Explanation

⊏⊸⍏⊕{⊃⊢⧻}⊸⊛⌵▽⌵⊸±­⁡​‎⁠⁠⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏‏​⁡⁠⁡‌⁢​‎⁠⁠⁠‎⁡⁠⁣⁤‏‏​⁡⁠⁡‌⁣​‎⁠⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏‏​⁡⁠⁡‌⁤​‎⁠⁠⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏‏​⁡⁠⁡‌­
 ▽⌵⊸± # ‎⁡remove non-letters
 ⌵ # ‎⁢uppercase
 ⊸⊛ # ‎⁣assign unique index to each unique value
 ⊕{⊃⊢⧻} # ‎⁤boxed group by letter count
⊏⊸⍏ # ‎⁢⁡sort
answered Jul 2, 2024 at 8:19
\$\endgroup\$
1
\$\begingroup\$

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.

answered Dec 3, 2024 at 15:35
\$\endgroup\$
1
\$\begingroup\$

JavaScript (Node.js), 95 (削除) 116 (削除ここまで) bytes

-21 bytes using golfing knowledge gained during the 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|=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 the first time)

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

answered Jul 7, 2023 at 9:47
\$\endgroup\$
0
\$\begingroup\$

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.

answered May 9, 2023 at 12:44
\$\endgroup\$
0
\$\begingroup\$

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)))))
answered Jul 7, 2023 at 13:30
\$\endgroup\$
0
\$\begingroup\$

AWK, 63 bytes

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

Try it online!

answered Dec 3, 2024 at 14:51
\$\endgroup\$
0
\$\begingroup\$

Raku (Perl 6) (rakudo), 30 bytes

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

Attempt This Online!

answered Apr 16 at 17:34
\$\endgroup\$
1
  • \$\begingroup\$ This appears to also count numbers and underscores, which the spec says should be ignored. \$\endgroup\$ Commented May 11 at 17:07
0
\$\begingroup\$

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 
answered May 12 at 20:47
\$\endgroup\$
0
\$\begingroup\$

Tcl, 106 bytes

proc C s {lmap c [split $s ""] {dict inc D [string tol $c]}
dict ma k\ v $D {if {$k!=" "} {puts $k:\ $v}}}

Try it online!

answered Nov 5 at 14:54
\$\endgroup\$
1
2

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.