# Text Statistics: Return a list/array of the number of occurrences of each character used in a text file, including spaces, tabs, and new lines [closed]

Generic Lorem Ipsum:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris felis dolor, bibendum at tincidunt vel, imperdiet eget libero. Cras vel felis purus. Phasellus a vestibulum enim. Sed eu orci ac nisl sodales scelerisque in id mi. Duis et nunc et nisl blandit aliquet ornare sed risus. Sed arcu magna, porttitor vel tincidunt quis, placerat non urna. Donec ut dolor eros, et viverra diam. Aliquam luctus, justo sed cursus imperdiet, velit nulla gravida nibh, vel scelerisque neque sem eget enim. Nunc in urna a velit adipiscing fermentum. Donec euismod faucibus purus. Vestibulum pretium, ligula commodo facilisis sollicitudin, nibh dolor adipiscing augue, nec tempus leo lectus eu enim. Vestibulum non erat velit, eget vulputate metus. Praesent at tincidunt elit. Add some stuff to it? !@#$%^&*()-_=+\|]}[{'";:/?.>,<~ Nunc pretium metus sed elit dignissim porta. Cras at dui quam. Mauris at elementum tellus. Sed placerat volutpat enim convallis egestas. Suspendisse consectetur egestas mattis. Proin interdum, lectus a tincidunt sollicitudin, mauris ligula accumsan nisi, et suscipit lectus enim id mi. In est ipsum, cursus nec eleifend id, sollicitudin ac sem. Nam augue felis, dapibus in faucibus sit amet, ultrices eget purus. Etiam suscipit mauris at velit fringilla molestie. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus faucibus tortor in libero convallis pharetra. Donec eros diam, tincidunt vitae euismod ut, bibendum non orci. Nullam quam tortor, accumsan id malesuada nec, dignissim vitae magna. Suspendisse mi odio, commodo ut tempor porttitor, congue sit amet risus. Praesent feugiat lacus quis orci sodales cursus. Vivamus ac lorem id sem iaculis interdum. • Do we need to distinguish things like LF and CRLF as different ways of separating lines? E.g. it's trivial for me to get a file line by line, but not byte by byte which makes this quite hard. I'm fine either way, but it should be clarified. – Joey Aug 7 '11 at 18:33 • -1. A problem statement should include a precise description of the problem, the input format (or permitted input formats), the valid range of inputs, the output format (or permitted output formats), and generally an example showing input and output. This has none of that. Aug 8 '11 at 9:58 • What is assumed about the characters? Are we in the area of UTF-8, or more in ascii/byte-land? Return a list/array seems overly specific - why not provide a complete (testable) program, which reads a file 'f', which is your message body, and prints the result in a readable form? Aug 9 '11 at 1:01 • I can't believe there are 11 answers already even though none of the answerers know what the question is. Aug 11 '11 at 11:42 ## 11 Answers ## PHP (16)(35) (28) The default mode of count_chars() returns "an array with the byte-value as key and the frequency of every byte as value." Edit: Oops, I just realized it's supposed to read in from a file. That adds 19 characters, so it might be beatable now. Edit 2: Reduced it by 7 characters. count_chars(join(file($s)));

• hard to beat this Aug 7 '11 at 17:18

## C: 50 chars (74 chars if done right)

"Return an array" is a pretty rare case when using C, because we can't actually return an array unless it's global or allocated by malloc(). So I decided to use a global one. The input is read from STDIN. The function works only on little-endian architectures.

n[256],r;*c(){while(read(0,&r,1))n[r]++;return n;}


The whole program that uses this function (with indentation added):

n[256],r;
*c()
{
n[r]++;
return n;
}
main()
{
int *num = c();
int i;
for (i = 0; i < 256; ++i)
printf("%d ", num[i]);
}


Yeah, I know I won't ever beat anybody with C.

EDIT:

There are some rightful remarks in comments, so I've rewritten the code so that it meets all the requirements. Now it's 74 chars long.

n[256],r,*f,*s;*z(){f=fopen(s,"r");while(fread(&r,1,1,f))n[r]++;return n;}


And the indented code with main() added:

n[256],r,*f,*s;
*z()
{
f=fopen(s,"r");
n[r]++;
return n;
}
main(int c,char**a)
{
int *stat, i;
s = a[1];
stat = z();
for (i = 0; i < 256; ++i)
printf("%d ", stat[i]);
printf("\n");
}

• Nice, but read() is not a standard function in C. Also the problem states (I think) that we should read a text file instead of stdin. Aug 7 '11 at 23:58
• @Harry K, the problem doesn't state anything. Aug 8 '11 at 9:56
• The title suggests reading from a file though. Aug 8 '11 at 21:52
• @Alexander Bakulin: you're missing an fclose(s); in the edited version. Aug 10 '11 at 17:59
• @Harry K.: according to the C99 standard, all opened files are closed before program termination (see 7.19.3-5), so it's perfectly legal. Aug 10 '11 at 18:08

This one is relatively simple. I chose to do it in Perl (my favorite scripting language). :)

## Perl: 41 characters 63 characters

Might be able to reduce it somewhat but this is what I'm starting with.

sub s{open(I,"<$_[0]");while(!eof(I)){read(I,$c,1);$s{$c}++}%s}


EDIT:
Got it down further:

sub s{foreach(split//,$_[0]){$s{\$_}++}%s}


## D (87 chars)

reads from a passed in file and uses binary reads like migimaru's php function

import std.stdio;int[] f(File i){int[256] r;foreach(c;i.byChunk(1))r[c[0]]++;return r;}


# Ruby, 41

F=->f{h=Hash.new 0
f.chars{|c|h[c]+=1}
h}


Accepting the assumption that the problem doesn't specify how the characters are read (according to Peter Taylor's comment on Alexander Bakulin's C suggestion) and assuming they are already read into a global array s, we can further reduce the C code in a portable way, by defining a function that accepts as an argument an already initialized char frequency table u[256], updates it and returns a pointer to it, like this:

# C - 39 chars (or 24 chars... see Edit below)

*r(int*u){while(*s)u[*s++]++;return u;}


And here is a sample, ungolfed program using the above function:

#include <stdio.h>
#include <stdlib.h>

// function selector
#define R_MORE_CHARS    0

// globals
int*s, u[256];

// ------------------------------------------------------------------------------------
#if R_MORE_CHARS
*r(int*u){while(*s)u[*s++]++;return u;}
#else
r(){while(*s)u[*s++]++;}
#endif
// ------------------------------------------------------------------------------------
{
int i, *s_ = calloc(maxfsize, sizeof(int));
if ( !s_ )
return NULL;

FILE *fp = fopen(fname, "r");
if ( !fp )
return NULL;

for (i=0; i < maxfsize && (s_[i]= getc(fp)) != EOF; i++)
;
s_[i] = 0;

fclose(fp);

return s_;
}
// ------------------------------------------------------------------------------------
void print_freq( int freq[256] )
{
int i=0;
for (; i<256; i++)
if ( freq[i] )
printf("ACII(%3d): %3d time(s)\n", i, freq[i]);
return;
}
// ------------------------------------------------------------------------------------
int main( void )
{
int *p;                 // for saving address of s
p = s = read_file("lorem.txt", 2000);   // get file contents into global s
if ( !s )
return 1;

#if R_MORE_CHARS
print_freq( r(u) );         // print updated frequency table
puts("\tmore chars: *r(int*u){while(*s)u[*s++]++;return u;}");
#else
r();                    // update frequency table
print_freq( u );
puts("\tless chars: r(){while(*s)u[*s++]++;}");
#endif

free( p );              // free saved s
return 0;
}


EDIT: Just thought that if we are to adjust the not so well defined problem to the language features, we could also completely skip the function arguments, working directly on the globals instead. Since in C we don't even need to return anything for the frequency table to get updated, the function can be further reduced down to 24 chars (i have also edited the sample program above, so now it can be used with either of those 2 functions...

# C - 24 chars

r(){while(*s)u[*s++]++;}


import List


### scala 164 156

val m=collection.mutable.Map[Char,Int]()
io.Source.fromFile("f").toArray.foreach(c=>m.update(c,m.getOrElseUpdate(c,0)+1))
m.mkString("\n")


Result:

> -> 1
+ -> 1
_ -> 1
{ -> 1
p -> 29
P -> 4
q -> 10
V -> 4
; -> 1
v -> 20


truncated to 10 lines.

## J - 26 characters (or perhaps 20)

The 26-character count is for the line that defines the function f.

NB. f takes a boxed filename,
NB. returns an array of integers counting occurrences of each character
NB. 26 characters

f=.[:<:[:+/"1[:=[:a.&,1!:1

NB. apply f to the file argument in jconsole textstats.ijs filename
A =. f 2{ARGV

NB. set output parameters so none of A gets elided
9!:37]0,~0,2#+:#0":A

NB. output A
exit echo A


Along the lines of Harry K.'s commentary, if we interpret the question to be about the function that computes the statistics, not also about I/O, f can go down to 20 characters:

f=.[:<:[:+/"1[:=a.&,

A =. f 1!:1]2{ARGV


f (in its shorter form) works like this:

a.&, : bond (via &) the alphabet a. (all characters J knows, in collating order) as the left argument to , (append); f's argument is the right argument to append

= : "self classify": this gives us a table where each row corresponds to a unique element of the argument (which is the result of "a. , arg ") and each column is 1 if that row's element appears in the same column of the argument, otherwise 0. For example, row 65 would have a 1 wherever an A appears at the same offset in the input.

[: in the context here lets us use = in its monadic (unary) sense, instead of of its dyadic/binary "equals" usage.

+/"1 : +/ applies addition between each pair of items, and "1 makes it do so for the items in each row, instead of using the rows themselves as the items. The result is each row becomes a count from the input of the character corresponding to the row.

<: : decrement all those counts, because they include the alphabet itself (a.).

Python 102 54 48 (87 79 78 with I/O)

As a pretty much newbie at this, and ignoring I/O:

from collections import*
r = Counter(s)
print(r)


The output is definitely not pretty though:

Counter({' ': 258, 'i': 155, 'e': 142, 's': 123,


Reading from file (removing a space between open() and "as" shaved one character):

from collections import*
with open("f")as f:
r=Counter(s)
print(r)


which generates the same output as the above example. I don't think I can get it smaller now, but there could be some function I've yet to explore.

Older solution:

import collections

d = collections.defaultdict(int)
for c in thestring:
d[c] += 1


The counting itself can probably be made shorter through some function I've not encountered/understood yet. I realize it's pretty much cheating by omitting the I/O part, but I'm going to hide behind my newbie tag for a while longer.

• from collections import*; f=lambda:Counter(open("f").read()).values() I had thought about posting this as a distinct answer, you're a newbie, so whatever. I do however want to note a few things: first, the problem states that you have to return a list, not to print it. Second, you shall return a list/array, not a dictionary/mapping as you did. This is why I called the Counter's values() method, which returns only the amounts of the distinct characters. Furthermore, you used lots of unnecessary variables, and that you used with would normally be good, but is just waste of space in a golf. Aug 12 '11 at 20:24
• @cemper93 Well that is certainly a lot shorter than my solution. I'm still learning, but I realized that I was mistaken on both returning a list (as well as Counter not yielding a list but a dictionary-type object). I've not had the chance to really understand lambda functions yet, and didn't even know that you could open a file without assigning it a variable. As for using with is just me trying to be proper. I suppose that's not the way I should think in golf. Aug 12 '11 at 21:24
• f = lambda a,b: a + b is just the same thing as typing def f(a, b): return a + b. You can also omit the f =-part and get a totally anonymous function, which is useful for e.g. the builtins filter, map and reduce (because they take functions as arguments and can be passed lambdas, too - so that filter(lambda x: x > 0, l) will remove all elements that are not > 0 from the list l. Aug 12 '11 at 21:37

## JavaScript, 55

Since JS can't read files, I'll assume that file is a function that returns the file as a string. (Requires Firefox!)

Array.reduce(file(),function(A,b)(A[b]=A[b]+1||1,A),{})
`