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!
47 Answers 47
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)
}
}
-
\$\begingroup\$ I don't know Scala, but I think
identitycan be replaced byx=>x\$\endgroup\$Kirill L.– Kirill L.2023年05月09日 14:24:32 +00:00Commented May 9, 2023 at 14:24
Thunno 2, 4 bytes
ỊLƈṠ
Explanation
ỊLƈṠ # Implicit input
Ị # Remove non-alphabets
L # Lowercase
ƈ # Counts
Ṡ # Sort
# Implicit output
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)->{})
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).
Raku, 21 bytes
*.lc.comb(/<:L>/).Bag
.lcconverts the input string to lowercase..comb(/<:L>/)produces a list of all non-overlapping matches for the regex<:L>(a letter) in that string..Bagconverts 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.
Pyth, 29 bytes
FNS{rzZI&<"`"N <N"{"(N /rzZN)
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.
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
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
Nibbles, 8.5 bytes (17 nibbles)
=~|.@`($\$a$~/$,ドル
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.
Uiua SBCS , 16 bytes
⊏⊸⍏⊕{⊃⊢⧻}⊸⊛⌵▽⌵⊸±
Explanation
⊏⊸⍏⊕{⊃⊢⧻}⊸⊛⌵▽⌵⊸±
▽⌵⊸± # remove non-letters
⌵ # uppercase
⊸⊛ # assign unique index to each unique value
⊕{⊃⊢⧻} # boxed group by letter count
⊏⊸⍏ # sort
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}
Returns a map of runes to ints.
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()
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.
Clojure, 47 bytes
#(frequencies(re-seq #"[a-z]"(.toLowerCase %)))
Returns a Map as allowed by OP in the comments. To obtain a sorted colllection this should be additionally wrapped with 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))))))
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:
- Read user input.
- Filter only the alphabetic characters.
- Convert all left-over characters into lowercase.
- Iterate left-to-right using a hash table.
- If the hash table has the character as a key, increment the key's value.
- Otherwise, add a new key representing the character and set the value to 1.
- 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)))))
-
\$\begingroup\$ This appears to also count numbers and underscores, which the spec says should be ignored. \$\endgroup\$Jordan– Jordan2025年05月11日 17:07:37 +00:00Commented May 11 at 17:07
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 ;
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
Mapacceptable? \$\endgroup\$