[2019/11/11 Update: sources moved to https://github.com/punsalad/projects/tree/master/bookpicker_simple GitHub]
[2018/07/03 Update: A newer version is described here. I'm leaving this description, and the scripts it describes, in place, though, because it's simpler.]
Another example of the mental aberration that causes me to write Perl scripts to solve life's little everyday irritants. In this case two little irritants:
- I noticed that I had a lot of books on my shelves, acquired long past, that I never got around to reading. Either because (a) they were dauntingly long and dense (I'm thinking about Infinite Jest by David Foster Wallace); or because (b) they just fell through the cracks. Both poor excuses, but there you are.
- I sometimes want to methodically read a series of books in a particular order.
In other words, I needed a way to bring diligence and organization to my previous chaotic and sloppy reading habits.
Here's how I went about scripting that:
I conceptualized my "to be read" books as a collection of book stacks, like the picture at (your) right (except more of them). Each stack is a list of books:
- either organized around a specific theme (usually an author) or is a
catchall (e.g. "non-fiction"); and
maintained in the order I want to read them.
(This goes back to the issue mentioned above: sometimes a series really "should"
be read in publishing order, for example C.J. Box's novels featuring
protagonist Joe Pickett.)
The implementation of this concept: each stack is a
file in my Linux directory
~/var/reading_lists. As I type,
sixteen of them:
(pas@oakland) ~/var/reading_lists: ls -l *.list -rw------- 1 pas pas 183 Oct 20 17:47 amber.list -rw------- 1 pas pas 41 May 17 18:05 asimov.list -rw------- 1 pas pas 242 Jul 25 06:09 box.list -rw------- 1 pas pas 93 Oct 9 12:27 connelly.list -rw------- 1 pas pas 43 Sep 7 10:28 conservative_lit_101.list -rw------- 1 pas pas 75 Sep 17 13:32 docford.list -rw------- 1 pas pas 46 Jun 30 11:12 elmore.list -rw------- 1 pas pas 83 Mar 29 2016 francis.list -rw------- 1 pas pas 266 Oct 28 06:52 genfic.list -rw------- 1 pas pas 65 Apr 13 2017 monkeewrench.list -rw------- 1 pas pas 144 Oct 16 17:11 moore.list -rw------- 1 pas pas 199 Oct 25 13:47 mystery.list -rw------- 1 pas pas 523 Oct 16 13:12 nonfic.list -rw------- 1 pas pas 56 Jul 18 15:04 reacher.list -rw------- 1 pas pas 333 Aug 30 15:37 sci-fi.list -rw------- 1 pas pas 45 Jun 11 15:50 winslow.list
Each list has one or more lines:
(pas@oakland) ~/var/reading_lists: wc -l *.list 6 amber.list 1 asimov.list 11 box.list 3 connelly.list 1 conservative_lit_101.list 5 docford.list 4 elmore.list 2 francis.list 8 genfic.list 4 monkeewrench.list 5 moore.list 6 mystery.list 13 nonfic.list 2 reacher.list 9 sci-fi.list 2 winslow.list 82 total
… and each line in each file contains a different book title. Example with
elmore.list, a list I created
in lieu of watching the six
seasons of Justified on Amazon Prime for the fourth time.
(pas@oakland) ~/var/reading_lists: cat elmore.list Pronto Riding the Rap Fire in the Hole Raylan
I.e., four books written by the late Elmore Leonard where Raylan Givens appears as a character.
The picking algorithm is simple and "works for me". When it's time to choose the next book to be read from this agglomeration, I pick a pile "at random" and take the book from the "top of the pile" (i.e., the one named in the first line of the file).
There is one more little tweak: the "random" pick is weighted by the
length of the list. So (for example) since there are 82 books total in
all lists above, and the
nonfic.list has 13 lines, a book
that list would be picked with probability
13⁄82. (Note the
probabilities calculated this way add up to 1, the probability
that some book from pile will be picked.)
That's not as hard as it might sound. I'd pseudocode the algorithm like this:
Let T be the total number of books in the lists, B0 + B1 + … + BN-1
Pick a random number r between 0 and T-1.
i = 0
while (r >= Bi)
r -= Bi
… and on loop exit i will index the list picked.
So: the "picking" script,
bookpicker, is here.
- You just run the script with no arguments or options.
- I left "debugging" print statements in.
- You're responsible for maintaining the lists; no blank/duplicate
- For the "picked" list, the script writes a smaller file with the
picked title missing. The old list is saved with a
.oldappended to the name. That's important, because next…
One last little gotcha: the randomization is sometimes a little too random. Specifically, sometimes after reading a book by a certain author, the picking script picks… the next book in the list by the same author. I don't want that. Variety is better.
So there's also a script to "undo" a previous pick,
bookpicker_unpick. If you run it before any other changes
are made to the list files, it will find the most-recently-modified
.list file, and "restore" the corresponding
The script, is here.