The Latest...

Replacing items in a list with... items from another list?

Posted: February 06, 2013 in Web Design

My office is just finishing up a major website overhaul.  One of the main points of the overhaul (other than providing a fresh "look" to the website) was to add some automation in.  As such, we put together a partial CMS (I say partial, because the content is still on the pages and not in a database) that holds contact info for each page, the page title, and a few other items.  The CMS also contains the staff listing, which helps to supply the contact details for the pages.

One of the cool things about the new system is that it automatically builds "breadcrumbs".  A trail of links that show how you got to where you are.  They build from the URL, so something like this:



Food → Sandwiches → Turkey and Swiss

After a few sweeps of a regex replace that swaps the underscores for spaces and provides the initial caps.

It all worked swimmingly, until of course it didn't.  Being a govenment agency, our office has acronyms.  LOTS of acronyms.  Also, the "and" up there in the previous example would have actually been "And".  I realized pretty quickly that we needed a list of exceptions, so I started compiling one, first as a list in ColdFusion, then as an XML file, then finally as a table in the database.

The exeptions list worked fine at first, but then it got crowded, and because of the way one of our former developers wrote the code (two recursive loops, one that looped every item in the breadcrumb list, and one that for each item in that list looped through every item in the exemption list).  This had its problems, the main one going something like this:

path: /food/sandwiches/pjb/pbj_on_wheat/

Let's pretent the exception list containes the following:

Pbj > PBJ

Pbj_On_Wheat > PBJ on Wheat

The first pass across the list matches up Pbj and replaces it with the caps version, but it also matches the Pbj in Pbj_On_Wheat, making it PBJ_On_Wheat.  When the second pass across the list comes, the Pbj_On_Wheat finds no matches (it's case-sensitive), so the change never gets made (and the O stays caps!).

Clearly, I needed something more accurate.  I came up with this:

(This assumes we've already parsed the URL with the first pass that replaces the underscores with spaces and adds the title case)

<cfset dirlist = "/Food/Sandwiches/Pbj/Pbj On Wheat/">
<cfloop query="exceptions">
   <cfif ListValueCount (dirlist,old,"/")>
   <cfloop from="1" to="#ListValueCount (dirlist,old,"/")#" index="i">
      <cfset dirlist = listsetat(dirlist,Listfind(dirlist,old,"/"),new,"/")>

The query "exceptions" is, as you'd expect, the list of exceptions, and it's cached, so there's only one database hit on the first page view.  ListValueCount() counts the number of matches each exception has in the list (which is the url), but ONLY the exact matches.  It won't find the Pbj in "Pbj 0n Wheat" if it's only looking for "Pbj". 

If there are no matches, it returns zero, so the second loop is avoided.  If it does find one (or more) matches, the next loop makes a pass for each match (ex. one passes for one match, two passes for two matches, etc), and the ListSetAt() replaces only the item at the spot that was matched.  In our case, Pbj is the 3rd item, so the 3rd item is replaced with the exception from the list.

Since it only finds exact matches, and only replaces what it finds, it's pretty accurate (and pretty speedy, since the exceptions list is cached), so it's ended up being a pretty good solution, and not a huge amount of code.  Can't ask for much more!

Add Comment

Click to reload a new image.

< Back to blog