A Filter to Display Neighbors in a List

January 20, 2009. Filed under django 72 design 5

When you don't spend much time thinking about them, sometimes it's easy to miss out on obvious UI patterns. This really struck me recently when I was making some minor tweaks to my blog.

In my last redesign I wanted to focus on articles as much as possible--modeling presentation after books and newspapers--and part of that was throwing out the sidebar. I eliminated as much redundant information as possible, but eventually reached a point where everything remaining seemed too important to cut.

I expanded the header to contain as much as possible, but knew I didn't have enough room to fit everything I wanted. So I decided to try making the header content contextual. Specifically, I broke the header into five segments.

Header design of Irrational Exuberance.

With the restrictions of spaces A, B, and C, it was pretty clear that I couldn't fit all of the content I wanted; I'd need to economize a bit. I decided to start out with a simple strategy that I could optimize on later:

  1. Always show additional nav links in slot C.
  2. If an article has a related series, show it in slot A.
  3. Backfill empty slots with related tags.
  4. Backfill an empty slot with recent entries.
  5. Backfill an empty slot with random entries.

This meant that navigation pages (like the Life page or the front) would always display the recent and random entries, and an entry would show fairly relevant articles (a mix entries in the same series and entries sharing the same tags). Although simplistic, these rules do seem to surface relevant content.

(I'm tinkering with the idea of replacing random entries with the top five entries by comment volume, which might do a better job of exposing interesting content.)

Example of header in Irrational Exuberance.

Since I had five navigational links in the C slot, I decided to include at most five articles in the A and B slots as well. Then I got mentally stuck for a few months: how could I handle a series of articles that was longer than five entries?

Somewhere after implementing half a dozen search prototypes at work I realized there is a pattern for this, and it exists in every search result page's UI.

Search pagination from search.yahoo.com

More specifically, what is interesting is what happens when you reach a page such that the first page of results is no longer being shown.

A deeper search pagination from search.yahoo.com

Ah, there it is: if I have more than five entries in a series, I just need to display the current entry, the two preceeding it, and the two following it. So blinding when I finally saw the light.

Implementation this concept as a template filter is fairly straightforward.

from django import template
register = template.Library()

def nearby(lst, obj, count=5):
    lst = list(lst)
    l = len(lst)
        pos = lst.index(obj)
    except ValueError:
        pos = 0
    dist = count / 2
    if pos <= dist:
        return lst[:count]
    if pos >= l - dist:
        return lst[l-count:]
        return lst[pos-dist:pos+dist+1]

register.filter('nearby', nearby)

Here is usage in a template (assuming an entry named object and a list of entries named series in the template context).

<h1>{{ object.title }}</h1>
<ol class="series">
{% for entry in series|nearby:object %}
<li>{{ entry.title }}</li>
{% endfor %}

I can only wonder how it took me so long to figure this one out.