Fun With Structures (Help Me Name This Post)
Posted By : todd sharp Posted At : January 30, 2008 11:54 AM Posted In: ColdFusion
4
I apologize for the extremely lame title of this post. It's worth reading, trust me. 250 bonus points to the commenter who comes up with the best alternate title.
Last night I was tightening up another release of my PPTUtils component when I hit a roadblock with one of the demos in the package.
The component returns an array containing an element that represents each slide in a PowerPoint presentation. One of the items in each element is the binary contents of the background image on the given slide. Due to the nature of PowerPoint, many of these images could be duplicates. When processing the array of slides I wanted a way to avoid writing these duplicate images to disk.
My first thought was to insert the binary data data into a struct and to then do a structFindValue() to see if that data already existed in the struct before inserting it. Since I was using the struct key name as the image name, if the data existed in the struct then I'd simply assign key name returned from structFindValue() as the image name (thus preventing a duplicate write to disk). The problem with this approach was that the image data was in binary form and structFindValue() can only search for simple strings in a struct. The fix was to convert the binary data to base64 and insert the base64 string into the struct. Since the duplicates would have an identical base64 representation this would allow me to find the image in the struct and avoid writing to disk again.
Seems a bit complex, but here's some code to make it a little easier to understand.
First I'll create a simple image:
<cfset one = imageNew("", 100, 100, "rgb", "red") />
<cfset oneAsBase64 = toBase64(one) />
<cfimage action="writeToBrowser" source="#one#" />
Which gives me:

Next create another image - but slightly different:

Now insert the base64 representations of those images in a struct and dump it:
<cfdump var="#testStruct#">

Now find the second image in that struct (imagine this step within a loop so as to avoid re-insertion).
Which gives me:

Here's the code in whole:
<cfset one = imageNew("", 100, 100, "rgb", "red") />
<cfset oneAsBase64 = toBase64(one) />
<cfimage action="writeToBrowser" source="#one#" />
<br />
<!--- create a new 100x100 image with a blue background --->
<cfset two = imageNew("", 100, 100, "rgb", "blue") />
<cfset twoAsBase64 = toBase64(two) />
<cfimage action="writeToBrowser" source="#two#" />
<cfset testStruct = {imgOne=oneAsBase64, imgTwo=twoAsBase64} />
<cfdump var="#testStruct#">
<br/>
<cfdump var="#structFindValue(testStruct,twoAsBase64)#">
So would there have been another way to do this? Sure! How about using the arrayFind() UDF from CFLib? This UDF uses the underlying Java indexOf() method on the array to check to see if the value exists.
Here's the altered code:
The arrayFind function uses the java.util.list indexOf function to find an element in an array.
v1 by Nathan Dintenfas.
@param array Array to search. (Required)
@param valueToFind Value to find. (Required)
@return Returns a number, 0 if no match is found.
@author Larry C. Lyons (larryclyons@gmail.com)
@version 2, August 10, 2007
--->
<cffunction name="arrayFind" access="public" hint="returns the index number of an item if it is in the array" output="false" returntype="numeric">
<cfargument name="array" required="true" type="array">
<cfargument name="valueToFind" required="true" type="string">
<cfreturn (arguments.array.indexOf(arguments.valueToFind)) + 1>
</cffunction>
<!--- create a new 100x100 image with a red background --->
<cfset one = imageNew("", 100, 100, "rgb", "red") />
<cfset oneAsBase64 = toBase64(one) />
<cfimage action="writeToBrowser" source="#one#" />
<br />
<!--- create a new 100x100 image with a blue background --->
<cfset two = imageNew("", 100, 100, "rgb", "blue") />
<cfset twoAsBase64 = toBase64(two) />
<cfimage action="writeToBrowser" source="#two#" />
<cfset testArr = [oneAsBase64,twoAsBase64] />
<cfdump var="#testArr#">
<cfdump var="#arrayFind(testArr,twoAsBase64)#">
<!--- outputs '2' --->



There's Always a Better Way With Java
oneAsBase64, twoAsBase64, threeAsBase64, you get the picture
Sometimes, All You Really Need is an Array
heh.