The general point is true, but the shell pipeline gets a lot more elegant if you use the sort-and-accumulate paradigm that the classic shell utilities were written for (which uses O(1) memory, by sorting on disk). Using mostly the author's own code, and adding --count to uniq:
tr < README.md --complement --squeeze-repeats '[:alpha:]' '\n' \
| tr A-Z a-z \
| nl \
| sort --key=2,2 --key=1,1n \
| uniq --skip-fields=1 --count \
| sort --key=2,2n \
| awk '{ print $3, $1 }'
(Where the final awk papers over the fact that we're mixing tabs and spaces here; obviously, awk is also good at doing the accumulation step, but uniq --count suffices here.)(I originally posted the above as a comment on lobste.rs, on this same article.)