Ask Ben: Converting a Query to an Array

Posted July 10, 2006 at 8:50 AM by Ben Nadel

Tags: ColdFusion, Ask Ben

A new ColdFusion programmer emailed me recently asking:

How can I convert a ColdFusion query into an array?

When it comes to this type of conversion, there are a few things that you have to take into account. For starters, is it worth it? The ColdFusion query object is a very powerful, very flexible array-like object to begin with. It can already be accessed directly without having to loop over the record set. Is this conversion something you really need to do? Second, if you do want to make the conversion, what structure do you want? You can either mimic the existing query notation by creating a "Structure of Arrays" or you can do what I feel is a more natural feeling conversion to create an "Array of Structures".

To begin with, let's cover how you can access a query object as a structure. Once you see this, you might realize that no conversion is needed at all. The ColdFusion query object can be accessed directly like a structure of arrays:

  • <cfset strValue = qRecords[ COLUMN_NAME ][ ROW_NUMBER ] />

The first index is the key value of the column you want to access, for example, "id" or "name". The second index value is the row for which you want to access that column. If you want to see that in english, think of it as "I want the value in the COLUMN_NAME column of the ROW_NUMBER row. After seeing this, you might not want to go through the processing overhead of converting the query object into any other structure as it is pretty awesome to start with.

After this, if you still want to convert the query object in to another structure, I suggest going to an array of structures. This adds memory overhead (over the structure of arrays method) since each row must have a copy of the column names (as look-up keys), but I think it is a much more natural way of thinking about the query set (especially if you are a traditional programmer).

In order to convert a query into an array, you basically have to create an array, loop over every row in the query record set, create a structure of the values in that row, and then append that structure to the array:

  • <cffunction name="QueryToArray" access="public" returntype="array" output="false"
  • hint="This turns a query into an array of structures.">

  • <!--- Define arguments. --->
  • <cfargument name="Data" type="query" required="yes" />

  • <cfscript>

  • // Define the local scope.
  • var LOCAL = StructNew();

  • // Get the column names as an array.
  • LOCAL.Columns = ListToArray( ARGUMENTS.Data.ColumnList );

  • // Create an array that will hold the query equivalent.
  • LOCAL.QueryArray = ArrayNew( 1 );

  • // Loop over the query.
  • for (LOCAL.RowIndex = 1 ; LOCAL.RowIndex LTE ARGUMENTS.Data.RecordCount ; LOCAL.RowIndex = (LOCAL.RowIndex + 1)){

  • // Create a row structure.
  • LOCAL.Row = StructNew();

  • // Loop over the columns in this row.
  • for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen( LOCAL.Columns ) ; LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){

  • // Get a reference to the query column.
  • LOCAL.ColumnName = LOCAL.Columns[ LOCAL.ColumnIndex ];

  • // Store the query cell value into the struct by key.
  • LOCAL.Row[ LOCAL.ColumnName ] = ARGUMENTS.Data[ LOCAL.ColumnName ][ LOCAL.RowIndex ];

  • }

  • // Add the structure to the query array.
  • ArrayAppend( LOCAL.QueryArray, LOCAL.Row );

  • }

  • // Return the array equivalent.
  • return( LOCAL.QueryArray );

  • </cfscript>
  • </cffunction>

To test, let's set up a simple query using an ID column and a NAME column:

  • <!--- Set up the query for testing. --->
  • <cfset qTest = QueryNew( "id, name" ) />

  • <cfset QueryAddRow( qTest ) />
  • <cfset qTest[ "id" ][ qTest.RecordCount ] = "1" />
  • <cfset qTest[ "name" ][ qTest.RecordCount ] = "molly" />

  • <cfset QueryAddRow( qTest ) />
  • <cfset qTest[ "id" ][ qTest.RecordCount ] = "2" />
  • <cfset qTest[ "name" ][ qTest.RecordCount ] = "Sophia" />

  • <cfset QueryAddRow( qTest ) />
  • <cfset qTest[ "id" ][ qTest.RecordCount ] = "3" />
  • <cfset qTest[ "name" ][ qTest.RecordCount ] = "Stefie" />

  • <cfset QueryAddRow( qTest ) />
  • <cfset qTest[ "id" ][ qTest.RecordCount ] = "4" />
  • <cfset qTest[ "name" ][ qTest.RecordCount ] = "Maud" />

Now, to convert that query to an array of structures, we simple call the QueryToArray() method and pass in the query as an argument.

  • <!--- Convert the query to an array. --->
  • <cfset arrTest = QueryToArray( qTest ) />

Dumping out the array, you can clearly see that it is an array or structures. Each structure at each array index has keys correlating to the query column.



Reader Comments

Aug 4, 2006 at 8:08 PM // reply »
9 Comments

Thank you for this.

"For starters, is it worth it?"
Yes, if you need to dynamically access the columns.
MyQuery[variables.col] unfortunately only works with Structs. :(


Dec 26, 2006 at 7:30 PM // reply »
2 Comments

Keeping up with 'Converting a Query to an Array' is tough nowadays...As part of the relaunch of their site, the folks at depressedpress.com (http://www.depressedpress.com) are presenting a series of CFML challenges. The first is converting a Query to an Array using only CFML as fast as you can. Merry Christmas of Poland!


Jan 12, 2007 at 6:23 AM // reply »
1 Comments

With regard to the comment:
MyQuery[variables.col] unfortunately only works with Structs. :(

Yes you can, but you need to suffix it with the row that you want, so it'd be MyQuery[variables.col][row]

If you're looping through the rows then just use the loop variable, or if you're inside a cfoutput or cfloop then just use currentRow.


Mar 1, 2007 at 10:40 PM // reply »
2 Comments

Nice solution, thank you.


Mar 1, 2007 at 10:41 PM // reply »
2 Comments

Nice solution, thank you.


Oct 30, 2007 at 4:43 PM // reply »
7 Comments

I was pulling my hair out trying to access a query column dynamically and saw this post. Thanks much for the tip!
It's funny how many years you can program CF and still find something new.


Dec 23, 2007 at 9:09 AM // reply »
1 Comments

THAT is exactly what i´m looking for! cool you are my daysaver, thank you very much for sharing its a great tutorial. merry xmas.


May 29, 2008 at 10:40 AM // reply »
4 Comments

Nice article. I used to count 1, 2, 3... but now I'm just counting 0, 1, 2... Thanks!


dcs
Jun 20, 2008 at 8:48 AM // reply »
24 Comments

Yes, if you need to dynamically access the columns.

Couldn't one also use the evaluate() function to dynamically access the columns? E.g.,

<cfset temp = evaluate("MyQuery.#variables.col#[#index#]")>

IIRC, inside a CFLOOP or CFOUTPUT of MyQuery one would only need to do the following:

<cfset temp = evaluate("#variables.col#")>

I actually prefer building queries (using queryNew(), queryAddRow(), etc.) to using arrays of structures or structures of arrays because queries are so powerful.


Jul 27, 2008 at 9:38 AM // reply »
1 Comments

Nice solution, thank you.


Jul 27, 2008 at 9:48 AM // reply »
1 Comments

Nice article. I used to count 1, 2, 3... but now I'm just counting 0, 1, 2... Thanks!


Aug 14, 2008 at 3:01 PM // reply »
1 Comments

Best answer I could find on the net on this topic. Thank you.


Aug 14, 2008 at 3:04 PM // reply »
10,638 Comments

@Mitran,

Awesome :)


Sep 8, 2009 at 10:33 AM // reply »
1 Comments

Thank you for this solution :-)


Oct 6, 2009 at 4:52 AM // reply »
1 Comments

thanks a LOT for this tutorial, it helped me ;)
searched quite a bit until i something usefull.


Feb 9, 2010 at 4:14 AM // reply »
1 Comments

Exellent script!

[code removed]


Feb 9, 2010 at 7:57 AM // reply »
10,638 Comments

@Stju,

Did you actually test this? I ask because there is a fatal flaw in it - you are using the same Row struct for every row. Since Structs are passed by reference, every subsequent update you make to it will change EVERY row already added to the array.

I just copy-pasted your code to confirm this. When I CFDump, every row is the same as the last one.

I am going to delete your comment so as not to mis-inform people. I appreciate the effort though.


May 27, 2010 at 5:25 PM // reply »
6 Comments

What about using the built in listToArray and ValueList functions?

myArray = ListToArray( valueList(myQuery.columnName) )

If there is any chance your values could contain a comma, then use a different delimiter:

myArray = ListToArray( valueList(myQuery.columnName,Chr(31) ),Chr(31) )


Jun 7, 2010 at 10:52 PM // reply »
10,638 Comments

@Ryan,

You could do that; but, that organizes the arrays column-wise, not row-wise. Not saying that's a problem - just pointing out the difference.


CF
May 13, 2011 at 1:43 AM // reply »
1 Comments

Not sure if anyone knows but to access dynamic column names you can do something similar to the following (quote and pound the dynamic column name as first array type element of query):

  • <cfset var cnt = 0>
  • <cfloop query="qry">
  • <cfset cnt++>
  • <cfloop from="1" to="#listLen(myDynamicFieldList)#" index="i">
  • cfset myDynamicColumn = qry["#listGetAt(myDynamicFieldList, i)]#"][cnt]>
  • </cfloop>
  • </cfloop>



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 3, 2012 at 10:49 PM
How I Got Node.js Running On A Linux Micro Instance Using Amazon EC2
Wow this was really helpful! Only thing I would add is you need to update your .bash_profile after you edit the secure_path. This is what I did: $ . ~/.bash_profile Otherwise, NPM won't be found. ... read »
Feb 3, 2012 at 10:14 PM
Pushing Base64-Encoded Images Over HTML5 WebSockets With Pusher And ColdFusion
@Ben, Just wanted to let you know that pusher are soon to start limiting sizes on messages. This was the detail that came through in the Feb dispatch: "However, we will soon be limiting the s ... read »
Feb 3, 2012 at 5:05 PM
Regular Expressions Make CSV Parsing In ColdFusion So Much Easier (And Faster)
I tried using your RegEx in my C# program, but it was matching an extra empty-string at the end and so I would end up with an extra field that doesn't exist, so I changed it to this: (^|,)("(?: ... read »
Feb 3, 2012 at 3:47 PM
ColdFusion Supports HTTP Verbs PUT And DELETE (As Well As GET And POST)
Josh Cyr posted this on Twitter just a little bit ago. Thought it was appropriate. http://stackoverflow.com/questions/1619152/how-to-create-rest-urls-without-verbs/1619677#1619677 ... read »
Feb 3, 2012 at 2:28 PM
Changing The Execution Context Of Your Self-Executing Function Blocks In JavaScript
@Michael, You definitely make a good point (and extra points for quoting movies - I love movies). When you use a return() statement to define the object's public API, it does provide a consistent a ... read »
Feb 3, 2012 at 2:04 PM
Changing The Execution Context Of Your Self-Executing Function Blocks In JavaScript
To quote Jurassic Park: "Just because you can doesn't mean you should". I completely, utterly disagree with the thought that this is more readable. Consider the current module pattern: if ... read »
Feb 3, 2012 at 1:10 PM
REST API Design Rulebook By Mark Masse
@Jordan, Yeah, WRML was created by Mark Masse (author of the book). I also found it to be a bit convoluted. I suppose it is intended to allow the Client to be able to programmaticaly respond to cha ... read »
Feb 3, 2012 at 1:08 PM
ColdFusion Supports HTTP Verbs PUT And DELETE (As Well As GET And POST)
@Jason, To be honest, I don't have good answers for that kinds of stuff. And, to the point, that is specifically why I *really* liked the REST API Design Rulebook by Mark Masse - he just cuts throu ... read »