24
\$\begingroup\$

Given a string of printable ASCII, output the frequency of each character in that string.

The Challenge

  • Input is given as a string of printable ASCII characters (decimal [32-126] inclusive).
  • Output the frequency of each character, in ASCII order.
  • The output must have a format similar to [character][separator][count]. Provided that there is a single, non-newline separating string between the character and its frequency, it is a valid output.
  • Output can be a single string, multiple strings, list of 2-tuples, array of tuples, etc.
  • Input and output can be given using any convenient method.
  • Standard loopholes are forbidden.
  • This is , so shortest in bytes wins.

Sample I/O

abcd
//outputs
a: 1
b: 1
c: 1
d: 1
Over 9001!
//outputs
  [ 1
! [ 1
0 [ 2
1 [ 1
9 [ 1
O [ 1
e [ 1
r [ 1
v [ 1
--<-<<+[+[<+>--->->->-<<<]>]<<--.<++++++.<<-..<<.<+.>>.>>.<<<.+++.>>.>>-.<<<+.
//outputs (as 2-tuples)
(+,14),(-,13),(.,13),(<,21),(>,13),([,2),(],2)
Su3OH39IguWH
//outputs (as 2d array)
[[3,2],[9,1],[H,2],[I,1],[O,1],[S,1],[W,1],[g,1],[u,2]]
\$\endgroup\$
  • \$\begingroup\$ Related and related. \$\endgroup\$ – bigyihsuan Jul 24 '19 at 5:00
  • 7
    \$\begingroup\$ Output the frequency of each character, in ASCII order. but e after v \$\endgroup\$ – Adám Jul 24 '19 at 6:21
  • 4
    \$\begingroup\$ May I output a,1b,2c,3? \$\endgroup\$ – tsh Jul 24 '19 at 7:40
  • \$\begingroup\$ Could I use PETSCII instead? -> c64-wiki.com/wiki/PETSCII Also, do you mean true ASCII (7-bit), or simply ASCII-compatible, like extended ASCII or UTF-8? \$\endgroup\$ – Shaun Bebbers Jul 24 '19 at 11:48
  • 1
    \$\begingroup\$ @ShaunBebbers ooo that's something. I'll allow it. ASCII-compatibles are also fine, provided you say what encoding it is. \$\endgroup\$ – bigyihsuan Jul 24 '19 at 12:47

43 Answers 43

1
2
1
\$\begingroup\$

Kotlin, 53 bytes

Returns a Map<Char, Int> associating characters to frequencies. The map keys are sorted in ASCII order (it's a java.util.SortedMap underneath.)

{s->s.associateWith{c->s.count{it==c}}.toSortedMap()}

Try it online!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Is this legal? I once got called out for defining my function in the header like you did here... Is that a legal code golf move because I'm genuinely not sure -> I can drop quite a few bytes on my Kotlin answer if so \$\endgroup\$ – Quinn Jul 25 '19 at 16:37
  • \$\begingroup\$ I'm not entirely sure what the consensus is but I recall it being legal for lambdas specifically. I've never been called out for it. \$\endgroup\$ – snail_ Jul 25 '19 at 21:45
  • \$\begingroup\$ This meta post states that for lambdas/anonymous functions only the function body is required. Since Kotlin can use type inference and you can indicate the function signature in the declaration, it seems to me like that counts as part of the assignment as well. \$\endgroup\$ – snail_ Jul 25 '19 at 22:28
1
\$\begingroup\$

Forth (gforth), 134 bytes

: f here 128 allot dup 128 erase -rot 0 do 2dup i + c@ + 1 swap +! loop drop
128 0 do dup i + c@ ?dup if i emit ." : ". cr then loop ;

Try it online!

Limitation: won't work if any character occurs more than 256 times in a single string (to fix this requires an extra ~25 bytes)

Explanation

  • Allocate an area of memory 128 bytes (cells if needed) wide to use as an array/dictionary
  • Zero out all bytes/cells in that area
  • Loop through string, for each character
    • Add ascii value to array starting address, then increment value at that address by 1
  • When finished, loop through array/dictionary and output all chars/values that have a value other than 0

Code Explanation

: f                     \ start new word definition
  here 128 allot        \ allocate 128 bytes to use as an array
  dup 128 erase         \ fill array with zeros
  -rot                  \ stick array address behind string address
  0 do                  \ start counted loop from 0 to string-length - 1
    2dup                \ duplicate array and string address
    i + c@              \ get ascii value of next character in string
    + 1 swap +1         \ add to array address, then add 1 to the value in that array position
  loop                  \ end the loop
  drop                  \ drop the string address [no longer needed]
  128 0 do              \ start a counted loop from 0 to 127
    dup i + c@          \ get value at next array position
    ?dup                \ duplicate if >0
    if                  \ start an if block (if >0)
      i emit            \ output the character
      ." : ".           \ output ": " followed by the number of that char in the string
      cr                \ output a newline
    then                \ end the if block
  loop                  \ end the loop
;                       \ end the word definition
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

R (in C locale), 32 bytes

function(s)table(strsplit(s,''))

Try it online!

strsplit the input character string by the empty string to separate its characters then table builds a contingency table of the unique character counts.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ identical(names(f("Over 9001!")), sort(names(f("Over 9001!")))) returns TRUE. \$\endgroup\$ – Rui Barradas Jul 25 '19 at 15:49
  • \$\begingroup\$ @Giuseppe Right. Besides, my example is meaningless, if table sorts according to the locale in effect, so will sort. \$\endgroup\$ – Rui Barradas Jul 25 '19 at 16:00
  • \$\begingroup\$ @Giuseppe Yes, like this users will have a bad surprise. \$\endgroup\$ – Rui Barradas Jul 25 '19 at 16:25
  • \$\begingroup\$ Something like this should be fine, just put a note saying "R (in C locale), 32 bytes" as the title, or something similar \$\endgroup\$ – Giuseppe Jul 25 '19 at 18:05
  • \$\begingroup\$ @Giuseppe Thanks, it's my first post and I really needed assistance. \$\endgroup\$ – Rui Barradas Jul 25 '19 at 18:48
1
\$\begingroup\$

Java (JDK), 82 bytes

s->s.stream().sorted().distinct().map(c->c+" "+s.stream().filter(x->c==x).count())

Try it online!

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 8 6 bytes

SêDŠ¢ø

Try it online!

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

JavaScript, 55  48  62 57 bytes

a=>[...a].sort().map(c=>o.set(c,-~o.get(c)),o=new Map)&&o

output is a Map

contrary to Objects, Map remember the order in which values where sets

let input = document.getElementById("input")
let run = document.getElementById("run")
let output = document.getElementById("output")

run.onclick = () => {
  output.innerHTML = ""
  func(input.value).forEach((val, key) => output.innerHTML += `'${key}' => ${val}<br>`)
}

let func = a=>[...a].sort().map(c=>o.set(c,-~o.get(c)),o=new Map)&&o
<input id="input"/>
<button id="run">run</button>
<p id="output"></p>

Thanks to Shaggy and Einacio for pointing my flaws

-5 bytes thanks to Shaggy

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Unfortunately, this doesn't seem to sort the output correctly. \$\endgroup\$ – Shaggy Jul 25 '19 at 23:32
  • \$\begingroup\$ I didn't see that I was confident object kept entries in the same order as they were added to it, I'll try to correct it later \$\endgroup\$ – jonatjano Jul 26 '19 at 7:14
  • \$\begingroup\$ i'm not sure about the input. it's defined in the question that it's a string, so splitting it should be part of your function as well \$\endgroup\$ – Einacio Jul 26 '19 at 7:18
  • \$\begingroup\$ seems like i misinterpreted the Input and output can be given using any convenient method. part :/ \$\endgroup\$ – jonatjano Jul 26 '19 at 7:20
  • \$\begingroup\$ +1 for Map; don't think I've seen it used here before. But you have a lot more parentheses than you actually need and you can ditch the get: tio.run/… \$\endgroup\$ – Shaggy Jul 26 '19 at 22:04
1
\$\begingroup\$

Japt, 10 8 bytes

ü
mÎíUmÊ

Try it

ü\nmÎíUmÊ     :Implicit input of string U
ü             :Split, sort and partition by value
 \n           :Reassign to U
   m          :Map
    Î         :  First element
     í        :Interleave with
      UmÊ     :  Map lengths of U
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Red, 92 bytes

func[s][parse/case sort/case s[any[copy k[copy t skip thru any t](print[t length? to""k])]]]

Try it online!

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Scala, 64 62 bytes

(x:String)=>x.groupBy(identity).mapValues(_.size).toSeq.sorted
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

C++ (gcc), 85 bytes

#include<map>
auto f(std::string s){std::map<char,int>m;for(int c:s)m[c]++;return m;}

Try it online!

This solution makes use of several nice features of std::map:

  1. It has a sorted iterator, with default lexicographic ordering.
  2. The []-operator default initializes members, in case of int it zeroes it.

Also, C++17 allows for golfing using auto and range-for loop.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Perl 5 (-nF -M5.01 -MList::Util=uniq), 49 41 bytes

41 bytes : for$a(uniq sort@F){say"$a ",eval"y/$a//"} doesn't work if input contains a slash. Otherwise, 36 bytes : for$a(uniq sort@F){say"$a "./\Q$a/g} doesn't work because of //g in scalar context.

for$a(uniq sort@F){say"$a ".(()=/\Q$a/g)}

TIO

first answer

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 44 bytes (and fewer commandline options, even though we don't count those now) \$\endgroup\$ – Xcali Jul 24 '19 at 17:07
  • \$\begingroup\$ 41 bytes (with some extra cli options) \$\endgroup\$ – Xcali Jul 25 '19 at 4:32
  • \$\begingroup\$ 9 characters longer but without the List::Util requirement: @a{@F}=1;for$a(sort keys%a){say"$a ".(()=/\Q$a/g)} \$\endgroup\$ – msh210 Jul 25 '19 at 23:17
  • \$\begingroup\$ @msh210, thanks for the trick @a{@F}=1 however first answer link is shorter also without List::Util, and options doesn't count now \$\endgroup\$ – Nahuel Fouilleul Jul 26 '19 at 7:52
0
\$\begingroup\$

SNOBOL4 (CSNOBOL4), 120 bytes

	A =&ALPHABET
	I =INPUT
S	O =0
	A LEN(1) . X REM . A	:F(END)
R	I X =	:F(O)
	O =O + 1	:(R)
O	OUTPUT =GT(O) X 0 O	:(S)
END

Try it online!

	A =&ALPHABET			;* alias for &ALPHABET (256 characters in ascending order)
	I =INPUT			;* read input
S	O =0				;* set counter to 0
	A LEN(1) . X REM . A	:F(END)	;* set X to first letter of A
					;* setting the REMainder to A
					;* and going to END if no letters remain
R	I X =	:F(O)			;* remove the first occurrence of X from I
					;* and output if none are left
	O =O + 1	:(R)		;* increment the counter, going to R
O	OUTPUT =GT(O) X 0 O	:(S)	;* if O > 0,
					;* output X 0 and O in that order
					;* then GOTO S
END
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

JavaScript (Node.js), 56 bytes

s=>[...s].sort().reduce((a,b)=>(a[b]=(a[b]||0)+1)&&a,{})

Try it online!

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Your output is incorrect. It is not in ASCII order, as can be seen with the input Over 9001!, where v is before e and r, which is not the case in ASCII order. Adding a .sort() would make it correct, at 56 bytes: s=>[...s].sort().reduce((a,b)=>(a[b]=(a[b]||0)+1)&&a,{}) \$\endgroup\$ – bigyihsuan Jul 26 '19 at 19:41
  • \$\begingroup\$ @bigyihsuan, thanks, indeed missed the ordering \$\endgroup\$ – 2oppin Jul 26 '19 at 20:17
1
2

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