are just as good for most things.
-----Original Message-----
From: Jim Davis [mailto:[EMAIL PROTECTED]
Sent: October 5, 2003 10:17 PM
To: CF-Talk
Subject: Sorting CFCs: Structures over Arrays in collections
Just finished a marathon coding session and thought I'd share/get some
opinions on some of the things I've found concerning CFCs and sorting.
I'm new CFCs and OO, so much of this may seem amateurish, but I expect
that many CFers are in the same position. I hope it may help somebody
or prompt discussion that heads off bad habits before they set in.
Specifically I've created many "base" domain components for this
application (a festival scheduling system) such as "Venue", "Event",
"Artist", etc. Each component has a corresponding "Collection"
component ("ArtistCollection" for example). These collection components
have the methods you'd expect: add(), drop(), getByID(),
isIDInCollection(), etc.
My goal is to load these collections will all the assets for a festival
and then use that data instead of hitting the database constantly. This
is all pretty standard fare as far as I know.
Right now I'm concerned only with the collection components.
I began, as everything I've read suggests, by creating an internal array
to hold the components. I now think that was a mistake and have
switched to structures to maintain the internal data. However
structures can't maintain an explicit order, something Arrays do by
design.
For this reason I use Structures internal to the component, but return
Arrays for most methods.
In my case the problem with using Arrays internally arose when I began
worry about sorted results. For example in my "VenueCollection" I want
to be able to return all artist components sorted by ID (the default),
Name, MapID (a numeric indicator which links the Venue to a graphical
map of the Festival) and so forth.
Sorting arrays of components is a pain. ArraySort() won't work on
arrays of complex objects so you're left with doing some sort of bubble
sort, going back out to the database, or using some sort of proxy
(creating, for example, a query using Query of Queries). All of these
are inelegant solutions in my opinion.
Also adding and dropping items from an array requires looping to
discover if the element exists before adding or deleting.
Structures on the other hand - they've got a lot going for them.
First off the Struct functions are much cleaner and simpler for most
tasks - at least if you have some unique ID you can use as the structkey
(the database key makes perfect sense).
For example to remove an element from an array you need to loop over the
array until you find it then do an ArrayDeleteAt(). With a Struct the
following line does it (this assumes that the Venue component to be
dropped has been passed to the Collection):
<cfreturn StructDelete(this.Venues, arguments.Venue.getVenueID(), true)>
This line returns "true" if the Venue existed in the collection, "false"
if it did not.
To add a component you could use StructAppend (which allows you to set
an overwrite behavior if you like) but I like to do this:
<cfif StructKeyExists(this.Venues, arguments.Venue.getVenueID())>
<cfreturn false>
<cfelse>
<cfset ArrayAppend(this.Venues, arguments.Venue)>
<cfreturn true>
</cfif>
I only want to add the component if it's not already in the component.
Again - with an Array you'd be looping to see if the component already
exists. You could augment this with StructUpdate() if you would like to
overwrite existing values.
The big win however comes when you want to sort the collection. CFML
lacks any "native" component collections or component functions.
However internally CF does see components as Structs (of a sort) and
many Struct functions can be bent to work on components.
Specifically in this case the StructSort() function will, in fact, sort
a structure full of components the same way it will sort a structure of
structures. This makes sorting a breeze.
For example, here's the meat of my VenueCollection's getSortedByName()
method
<cfset var curSort = StructSort(this.Venues, "text", "ASC", "Name")>
<cfloop from="1" to="#ArrayLen(curSort)#" index="cnt">
<cfset curSort[Cnt] = cur.Venues[curSort[Cnt]]>
</cfloop>
<cfreturn curSort>
The first line does all the work. "this.Venues" is a structure
containing all the Venues in the collection. "Name" is a simple
property of Venue. One note: any property you want to sort on MUST be
present in the "this" scope - the component's variables (local) scope is
not exposed to the StructSort() function (an exception will be thrown).
Anyway - the first line does the sort, then returns a sorted array
containing (at this point) only the structure keys. The 3rd-5th lines
loops over that array and populates it with references to the components
instead. The last line returns the newly created Array of sorted
component references to the caller.
In my case I only wanted to sort on a few specific properties, however
it would be simple to convert the above to accept an argument of any
property thus creating a generic "sort" method.
So, in the end, external calls to the collection generally return arrays
- ready to loop over, present or whatever; but internally everything is
stored as a structure - which offers a lot more flexibility and reduces
the code needed to manage things.
It took me a while to replace all of my arrays with structs, but it's
already paying off big time. The components are faster, smaller and
easy to extend.
As always I'd love to hear opinions on this from those that know more
than I do (which includes... well, pretty much everybody). Are there
actually good reasons to use arrays rather than structs?
Jim Davis
[Todays Threads] [This Message] [Subscription] [Fast Unsubscribe] [User Settings]

