My Book Picker (and Unpicker)

[2019/11/11 Update: sources moved to 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:

  1. 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.

  2. 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:

  1. either organized around a specific theme (usually an author) or is a catchall (e.g. "non-fiction"); and

  2. 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 .list 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
Riding the Rap
Fire in the Hole

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 from that list would be picked with probability 1382. (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:

Given: N lists (indexed 0..N-1) with Bi books in the ith list…

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. Notes:

  • 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 lines, etc.

  • For the "picked" list, the script writes a smaller file with the picked title missing. The old list is saved with a .old appended 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 .list.old file. The script, is here.

Last Modified 2019-11-11 6:31 AM EDT