Listmaker

For Efficient Traversal of the Grocery Store

[2021-07-13 Update: Didn't take long after my initial post to make some major changes. Embarassing that I didn't get it right the first time.]

Another Perl/Linux-based salve to the mental aberration I've mentioned in the past: using my meager coding skills as a hammer to whack down life's occasional slightly-annoying nails. Specifically, grocery shopping. It's partially my job. Mrs. Salad provides me with a handwritten list. A recent example:

Example List

Nice handwriting, right? Yes, she really calls those cookie dough packages "plunk & bake". And this example is neater than average, shorter than average, and pretty well organized. Still… Often I'll be finishing up shopping in Aisle 13 of the Dover, New Hampshire Hannaford … and suddenly realize that I missed getting something back in Aisle 2.

(Or sometimes not realizing I missed items until I get home.)

What I wanted was a list organized in the order in which I actually go through the store, separated into aisles (or departments) to make it easy to check that I've gotten (for example) all the Aisle 2 items before I move on to Aisles 3, 4, …

Something like this, an HTML table:

Loc/Aisle Qty Item Notes
4   Brownie Mix
    Jambalaya Mix Large
5   Raisin Bran Crunch
    Pineapple Tidbits
    Rice Krispies
6 2 V8
8   Incredibites Dry, Chicken
Back Wall   Milk
    Oatly
11   Bread Artesano
13   Cookie Dough
    Yogurt
    Pie Crust
    Ice Cream Cherry Vanilla
    Outshine Coffee Bars

In fact, exactly like that. You might notice I've added a couple items of my own; I'm in charge of keeping track of pet food, Raisin Bran Crunch, V-8, and a few other things.

Hence this script, listmaker; it produces a suitable-for-printing HTML list organizing the listed items into the order I traverse the store. Typically that's in ascending-aisle order, but including the departments (Deli, Meat, Seafood, Bakery,…) on the store's periphery. Before I leave (say) Aisle 2, it's easy to verify that I've picked up everything I was supposed to get in Aisle 2.

The workflow is simple. First, I transcribe the handwritten list into a text file:

2 V8
Raisin Bran Crunch
# Orange Juice
# Eggs
# Coffee
Cookie Dough
Milk
Yogurt
Oatly
Pie Crust
Brownie Mix
Pineapple Tidbits
Jambalaya Mix | Large
Rice Krispies
Bread|Artesano
Ice Cream|Cherry Vanilla
Outshine Coffee Bars
Incredibites|Dry, Chicken

The syntax is simple, informal, and flexible:

  • One "item" per line.
  • Lines starting with a pound sign (#) are comments, and are ignored. Used for commonly-bought items; just remove the pound sign to include them, add one to exclude them.
  • A leading digit string designating quantity is optional. Of course, a missing number implies quantity 1.
  • An optional "Notes" field is text following a vertical bar (|). This can be used in many ways: specifying a brand, size, flavor,… Notes go in a separate column in the HTML table.

Once the list is transcribed, the script can be run. Example, assuming the transcribed list above is in the file $HOME/Documents/mylist:

$ listmaker ~/Documents/mylist
[HTML list saved at file:///home/pas/Documents/mylist.html]

As a somewhat arbitrary design choice, the HTML output file is written to the same directory containing the list, with the .html extension tacked on.

I use a "store configuration file" for store-specific details. It contains Perl initialization code for two hashes:

  • %ORDER which specifies the order in which I visit aisles/departments:

    %ORDER = (
        '10'        => 14,
        '11'        => 16,
        '12'        => 17,
        '13'        => 18,
        '1'         => 4,
        '2'         => 5,
        '3'         => 6,
        '4'         => 7,
        '5'         => 8,
        '6'         => 9,
        '7'         => 10,
        '8'         => 12,
        '9'         => 13,
        'Back Wall' => 15,
        'Bakery'    => 1,
        'Deli'      => 2,
        'Front End' => 21,
        'Hbc 4l'    => 20,
        'Meat'      => 11,
        'Pharm'     => 19,
        'Produce'   => 0,
        'Seafood'   => 3,
    );
    

    In this case: the visitation order is: Produce, Bakery, Deli, Seafood, Aisle 1, 2, … (I've used perltidy to prettify the actual file.)

  • %HMAP which maps item names to aisles/locations. It contains many lines, here's a sample:

    
    %HMAP = (
        'v8'                 => '6',
        'raisin bran crunch' => '5',
        'brownie mix'        => '4',
        'pastrami'           => 'Deli',
        'ice cream'          => '13',
        […]
    );
    

    This hash can get messy and possibly redundant, especially if you (like me) are not consistent or careful in how you specify items. It's easy enough (if somewhat tedious) to clean up with a text editor.

The configuration file is loaded into the script with a Perl do command. Some basic sanity checks are performed.

The default location for the configuration file is $HOME/etc/listmaker.cf. The idea here is that if you want to use this script for more than one store, you use different configuration files. A non-default configuration file is specified to the script with the -s option, for example:

$ listmaker -s ~/Walmart.cf mylist

This is a project in my Github repository; the script is here. Notes:

  • The script uses the HTML::Template CPAN module to produce its HTML output. The template is pretty straightforward and it is here.

  • If there's an item on the script not found in the configuration file, the script will ask that you provide an aisle/location for it. Good news: your response will be used to update the configuration file, so you won't need to do that in the future. (Specifically, the script uses the Data::Dumper Perl module to produce new initialization code for the hashes described above, written back out to the file.)


Last Modified 2021-07-13 6:38 AM EDT