• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Array of Structures problem

New Here ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

I am trying to create a chronological event calendar from events drawn from three different queries. The following code creates the array and populates it properly:

<CFSET EventArray=ArrayNew(1)>
<CFSET EventInfo=StructNew()>
<CFSET ArrIndex=1>

<CFLOOP QUERY="EventQuery1">
<CFSET EventInfo.ID=EventQuery1.ID>
<CFSET EventInfo.StartDate=EventQuery1.StartDate>
<CFSET EventInfo.EventType=EventQuery1.EventType>
<CFSET EventArray[ArrIndex]=EventInfo>
<CFSET ArrIndex=ArrIndex+1>
</CFLOOP>

<CFLOOP QUERY="EventQuery2">
<CFSET EventInfo.ID=EventQuery2.ID>
<CFSET EventInfo.StartDate=EventQuery2.StartDate>
<CFSET EventInfo.EventType=EventQuery2.EventType>
<CFSET EventArray[ArrIndex]=EventInfo>
<CFSET ArrIndex=ArrIndex+1>
</CFLOOP>

<CFLOOP QUERY="EventQuery3">
<CFSET EventInfo.ID=EventQuery3.ID>
<CFSET EventInfo.StartDate=EventQuery3.StartDate>
<CFSET EventInfo.EventType=EventQuery3.EventType>
<CFSET EventArray[ArrIndex]=EventInfo>
<CFSET ArrIndex=ArrIndex+1>
</CFLOOP>

The problem I'm having is that when I try to loop through the fully populated array, I can only retrieve data from the last array entry. For example, the following code will only display the StartDate of the last event read:

<CFSET LoopCount = ArrIndex-1>

<CFLOOP CONDITION="LoopCount GREATER THAN OR EQUAL TO 1">
<CFOUTPUT>#LoopCount#</CFOUTPUT> &nbsp
<CFOUTPUT>#EventArray[LoopCount].StartDate#</CFOUTPUT>
<CFSET LoopCount=LoopCount-1>
</CFLOOP>

How can I get this loop to read all array entries? My ultimate goal is to display all events chronologically by StartDate.
TOPICS
Advanced techniques

Views

507

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

From a syntax perspective, when you loop through a query using cfloop (as opposed to cfoutput), you have to specfify the rownumber. The syntax is queryname.fieldname[rownumber]

Given your ultimate goal, your approach seems way too complicated. The fact that you need 3 queries seems suspicious, but, if they really are necessary, you could alway use Q of Q to combine them into one query. Then you probably would not need an array of structures.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

The loop that is causing me problems is over an array (of structs), not a query.

I using a single query to retrieve the data, but ran into problems with that approach, too. I tried to compare the EventInfo.EventType elements with know valid constants, but could never get the results I expected. I figured that the table type I'm using (DBase IV) added some null characters, but haven't been able to prove it.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

BINPA:

Dan's right that a query of queries seems like it might be more logical here, but in any event, you're overwriting your array values for each CFLOOP tag. In other words, if you want output from EventQuery2 to append to the array (rather than to overwrite values just set by EventQuery1), you need to ensure that you're dynamically generating a new index position for each iteration, of each CFLOOP occurence in the code block. Ditto for output from EventQuery3 overwriting values set by EventQuery2 (which explains why you can only output values set by EventQuery3).

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

davidsimms:

I thought the lines:

<CFSET EventArray[ArrIndex]=EventInfo>
<CFSET ArrIndex=ArrIndex+1>


at the end of each loop would be enough to ensure that each array index position was unique. Is there something else I need?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

What do you get if you <cfdump var="#EventArray#"> after you are done
building the array.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

You may also be running into an pass by reference problem.

When you create the structure with the <cfset EventInfo = StructNew()>
This creates a structure, assignes it some memory and references the
variable name 'eventInfo' to that memory location.

When you then Assign the eventinfo to an element of the eventarray, it
just assigns another pointer to the same memory. So all your eventArray
elements point to the same EventInfo memory location which has the last
set of data in it. I suspect you will see this when you dump the array.

Try this
<CFSET EventArray[ArrIndex]=dupliate(EventInfo)>

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advisor ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

Any time you find yourself repeating blocks of code, alarm bells should go off on your head.
Think modular code and functions! (The previous arguments about Q of Q, etc., are good too.)

Look at the attached code. It:
(1) Works (avoids the pass by reference issue)
(2) Is shorter.
(3) Is modular -- meaning it's easier to maintain.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

<CFFUNCTION name="AddEventQryToArray" returntype="array">
<CFARGUMENT name="aTargetArray" type="array" required="yes">
<CFARGUMENT name="qSrcQry" type="query" required="yes"
hint="Must
contain columns: ID, StartDate, and EventType">

<CFSET var zTmpStruct = StructNew()><!--- create struct here --->

<CFLOOP query="arguments.qSrcQry">
<CFSCRIPT>
zTmpStruct.ID = arguments.qSrcQry.ID;
zTmpStruct.StartDate = arguments.qSrcQry.StartDate;
zTmpStruct.EventType = arguments.qSrcQry.EventType;
ArrayAppend (arguments.aTargetArray, zTmpStruct);
</CFSCRIPT>
</CFLOOP>

<CFRETURN arguments.aTargetArray>
</CFFUNCTION>

Just to be a bit nitpicky, the function code provide by MikerRoo avoided
the pass by reference problem by defining a new struct with the
structNew function for each iteration of the loop inside the function.
Which is the way I would have done it as well.

But, in order to avoid doing real work, I wanted to point out that with
the small change provide above, this User Defined Function (UDF) would
suffer a similar pass by reference issue. So it was MikerRoo's choice
of where to create a new structure, rather then his choice of doing it
in a UDF that resolved the pass by reference issue.

All other points about using an UDF are very valid.



Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

> From a syntax perspective, when you loop through a query using cfloop (as
> opposed to cfoutput), you have to specfify the rownumber.

No you do not.

--
Adam

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 02, 2006 Oct 02, 2006

Copy link to clipboard

Copied

On Mon, 02 Oct 2006 13:14:07 -0700, Ian Skinner wrote:

> You may also be running into an pass by reference problem.

Yep. That's what it looks like to me.

> Try this
> <CFSET EventArray[ArrIndex]=dupliate(EventInfo)>

Typo aside, that's pretty much it, I reckon. Although I'd dispense with
maintaining the ArrIndex variable, which can be replaced with simply using
arrayAppend(). The counter seems to be serving no purpose other than
pointing to the end of the array: CF already knows where that is, so
there's no need to keep track of it separately.

I concur with everyone who says the whole construct (ie: the originally
posted code) looks a bit clunky though.

Why are you building an array of identically-keyed structs out of a query,
instead of leaving it as a query? As someone else said, if you MUST have
three separate queries (doubtful), then just QoQ them together. Better
yet, UNION them at database level.

--
Adam

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Oct 03, 2006 Oct 03, 2006

Copy link to clipboard

Copied

LATEST
This is the result of "smart" Cold Fusion design, where everything is passed by value, except structures...

Just change your code slightly. Move StructNew() inside the loop like this:

<CFSET ArrIndex=1>
<CFLOOP .. .. .>
<CFSET EventArray[ArrIndex]=StructNew()>
<CFSET EventArray[ArrIndex].ID=EventQuery1.ID>
. . . . . . . . . . . . . . . .
<CFSET ArrIndex=ArrIndex+1>
</CFLOOP>

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Resources
Documentation