Ask Ben: Parsing Nested Lists With A Single Delimiter In ColdFusion

Posted September 22, 2009 at 10:17 AM by Ben Nadel

Tags: ColdFusion, Ask Ben

Ben, How would I go about splitting a list into multiple lists? For example I have a list:

07/08/2009|1,573,067.20|8/8/2009|1,563,000.20

This list can be infinite in values but will always contain a date/amount combination. I need to split these into lists of 2 one date/amount lists. Seems like it should be easy enough to do but I think I have been looking at it for too long. Any help you can give would be greatly appreciated.

Since you have nested lists (a list of lists) but only a single delimiter, we cannot look at this value as list just yet; at least, not in the most usable way. To make this data usable, we have two options. Since you hinted at wanting to split this into multiple lists, I'll explore, as our first option, looking at this list of lists as a pattern in which we have a date value followed by a pipe followed by an amount value. Or, to abstract it out even more, a non-pipe value followed by a pipe followed by a non-pipe value. With this pattern mentality, we can then quite easily extract all of the sub-lists from this master list despite the fact that there is only one delimiter:

  • <!---
  • Create the data set (I am using string concatenation here
  • only for display reasons).
  • --->
  • <cfset data = (
  • "07/08/2009|1,573,067.20|8/8/2009|1,563,000.20|" &
  • "9/8/2009|1500000|10/8/2009|800000"
  • ) />
  •  
  • <!---
  • Right now, we have a HUGE list that has a single delimiter
  • (|). As such, we cannot really think of this as a list yet.
  • But, we can think of it as a pattern; a date followed by an
  • amount, separated by a pipe. With this mentality, we can
  • use the reMatch() method to extract the sub-lists from our
  • larger list.
  •  
  • Basically, the pattern is simple: a value that does not
  • contain the pipe (our date) followed by the pipe, followed
  • by a value that does not contain the pipe (our amount).
  • --->
  • <cfset pairs = reMatch(
  • "[^|]+\|[^|]+",
  • data
  • ) />
  •  
  • <!---
  • At this point, our pairs contains a bunch of smaller lists,
  • each with two values: our date and our amount. This list is
  • pipe-delimited and can be easily parsed:
  • --->
  • <cfloop
  • index="pair"
  • array="#pairs#">
  •  
  • Date: #listFirst( pair, "|" )#,
  • Amount: #listLast( pair, "|" )#
  • <br />
  •  
  • </cfloop>

Once we extract the sub-lists from the master list, our resultant array contains many small values, each of which is in the form of "date-pipe-amount". These values can then be iterated over and treated as a clean, pipe-delimited list. And, when we run the above code, we get the following output:

Date: 07/08/2009, Amount: 1,573,067.20
Date: 8/8/2009, Amount: 1,563,000.20
Date: 9/8/2009, Amount: 1500000
Date: 10/8/2009, Amount: 800000

This works quite nicely.

If you are not tied to sub-lists, our second, perhaps cleaner, option would be to take this list and split it into a single array; this method will be faster and even a bit more accessible as it does not require any understanding of regular expressions:

  • <!---
  • Create the data set (I am using string concatenation here
  • only for display reasons).
  • --->
  • <cfset data = (
  • "07/08/2009|1,573,067.20|8/8/2009|1,563,000.20|" &
  • "9/8/2009|1500000|10/8/2009|800000"
  • ) />
  •  
  • <!---
  • Split the entire data value into an array on the pipe
  • delimitter. Once we do this, our date/amount pairs will
  • be in successive indexes of the resultant array.
  • --->
  • <cfset parts = listToArray( data, "|" ) />
  •  
  • <!---
  • At this point, our entire data list has been broken out into
  • tokens. Loop over the array, incrementing by 2 since each two
  • indeciis are related.
  • --->
  • <cfloop
  • index="index"
  • from="1"
  • to="#arrayLen( parts )#"
  • step="2">
  •  
  • Date: #parts[ index ]#,
  • Amount: #parts[ index + 1 ]#
  • <br />
  •  
  • </cfloop>

As you can see, once we split the master list on the pipe-delimiter, our array now contains all the list items in which are sub-lists are represented by successive array indices. Looping over the array by increments of two allows us to easily grab those sub-list values. And, when we run the above code, we get the same output:

Date: 07/08/2009, Amount: 1,573,067.20
Date: 8/8/2009, Amount: 1,563,000.20
Date: 9/8/2009, Amount: 1500000
Date: 10/8/2009, Amount: 800000

I am a huge fan of regular expressions, but they are probably not the right solution for this job. I would most likely end up going with method two. I hope this helps!



Reader Comments

Sep 22, 2009 at 4:44 PM // reply »
5 Comments

@Ben
This is interesting because it is one of those types of things that could be solved in many different ways. I would've probably taken the low tech approach of creating a new array and then looping through the list and stuffing values in their respective columns based on whether the index of the array was even or odd - which would probably work because we've only got two columns in this example. Any post that makes you stop and say "How would I do that?" is a good one!


Sep 23, 2009 at 8:02 AM // reply »
10,640 Comments

@Andy,

Yeah - very true - there are number of ways to solve this kind of problem; that's part of the reason why I try to never view a question as too simple; often times, just the act of answering it generally precipitates new thoughts on how to solve it.


JC
Sep 23, 2009 at 6:20 PM // reply »
16 Comments

I probably would have used MOD... if list item number is even, name, odd, value... something like that. But this is probably a better way. :)


Sep 24, 2009 at 9:18 AM // reply »
10,640 Comments

@JC,

The MOD operator certainly rocks though :)


Oct 21, 2009 at 9:59 PM // reply »
53 Comments

@Ben,

Awesome solutions!

I would have never thought to look at this as a pattern. I like the first approach, mainly because I think regex is amazing.


Oct 31, 2009 at 4:05 PM // reply »
10,640 Comments

@Andrew,

Regex definitely is amazing :)


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »
Feb 9, 2012 at 10:29 PM
Learning ColdFusion 9: Application-Specific Data Sources
@Ben, No offence, but if people were really wanting advanced features they would be using a platform like ASP.NET MVC. CFML is so structurally compromised as a tag-based scripting language that ... read »
Feb 9, 2012 at 10:03 PM
Subversion - Cleanup Failed To Process The Following Paths
@Leviaguirre, do you still have problems with this? ... read »