Jump to content
Developer Wiki and Function Reference Links ×

Wallowing through arrays of structures


J. Miller

Recommended Posts

Hello,

I am trying to build a Summary array (xArraySummary} from an array of Lighting Fixtures (xArray)

I want to get a total (IntelCnt) of each type of Fixture (IntelInstType)

The xSummaryArray is to be used in my script to build a list of instruments in a dialog

Being more of a dabbler and not an ace programmer I was wondering if there is an easier way to do this (nested loops ?, which always make me loopy}

Does this make sense to anyone?

{************************** SAMPLE CODE ***************************************}

TYPE

IntelData = STRUCTURE

IntelHan, IntelRecHan : HANDLE;

IntelInstType : STRING;

IntelCnt : INTEGER;

END;

CONST

kMaxArray = 200;

VAR

xArray, xArraySummary : Array[1..kMaxArray] of IntelData

intNum, arrayCnt , intVar : INTEGER;

holderString : STRING;

IF intVar > 0 THEN BEGIN {intVar determines if sort should happen}

xArraySummary[1].IntelInstType := xArray[1].IntelInstType; {put 1st xArray Element into xArraySummary}

intNum := 1; {declare variable}

arrayCnt := 1; {declare variable}

FOR i := 1 TO noNumChan DO BEGIN {loop thru xArray}

holderString := xArray.IntelInstType;

arrayCnt := 1;

WHILE arrayCnt <> kMaxArray DO BEGIN

{IF NAMES ARE THE SAME GET OUT OF LOOP}

IF xArraySummary[arrayCnt].IntelInstType = holderString THEN BEGIN

xArraySummary[arrayCnt].IntelCnt := xArraySummary[arrayCnt].IntelCnt +1; {counts qty of fixtures}

arrayCnt := kMaxArray; {kill the while loop}

END

{IF NAMES DONT MATCH ADD 1 to arraycnt to look at next element}

ELSE IF (xArraySummary[arrayCnt].IntelInstType <> holderString) AND (xArraySummary[arrayCnt].IntelInstType <> '') THEN BEGIN

arrayCnt := arrayCnt +1;

END

{IF NAMES DONT MATCH AND IS BLANK PUT NAME IN ARRAY}

ELSE BEGIN

xArraySummary[arrayCnt].IntelInstType := holderString;

xArraySummary[arrayCnt].IntelCnt := xArraySummary[arrayCnt].IntelCnt +1; {counts qty of fixtures}

intNum := intNum +1; { will determine how many fixtures i need to list in my dialog}

arrayCnt := kmaxArray; {kill the while loop}

END;

END;

END;

END;

{******************************** SAMPLE CODE *************************************}

Thanks

Jeff Miller

Link to comment

I don't know if this is simpler or not, but I think this can be done with two nested For statements.

I have simplified this (and obviously not run it) to skip the field part of the array filling and matching.

This version avoids the need to set the counters high to artificially kill the loop.

Pat

Begin
 xArraySummary[1] := xArray[1];
 arrayCnt :=1;

 For i := 2 to noNumChan do
   Begin
     k:=0;  {set counter to see if we have processed every item in the Summary]
     For j:= 1 to arrayCnt do  {set counter for number of rows in Summary]
       Begin
         If xArraySummary[j] <> xArray[i] do k := k +1;  {increment for rows that don't match}
       End;
     If k = arrayCnt do  {if k is the same as the arrayCnt then we did not match in Summary}
       Begin
         arrayCnt := arrayCnt +1;
         xArraySummary[arrayCnt] := xArray[i];
       End;
   End;
End;

Link to comment

Are you doing something else with xArray besides summarizing it with another array? IE does xArray exist only to be summarized?

If so, I'd do the counting while loading xArray. The loading procedure looks at an object. If it's not been seen before it adds it to the array. If the name is on the array it increments the count and moves on to next object.

You'd need a two dimensional array. Column one is intelCount and two is intelData.

Of course, you may have needs that disqualify this approach, but if not it seems a lot simpler to me. The entire loading and counting is done within one loop, there's one less array to manage, and one less array to traverse.

My apologies if I'm stating the obvious.

Link to comment

Here's how I've looked at values and added to array, or counted them if they were already in the array. It's the same approach if the values are in another array, or the values are derived from a PIO.

In my EG I already have two procedures: sameValue and newValue. You already know what each does. You could write out the procedures directly in the loop if it made more sense to you.

In any case, the last thing that happens in each is that a boolean becomes true. If conditions are met and either procedure runs then 'done' becomes true, we exit the loop and move on to the next value to either be counted or added to the array.

If neither condition is met then done stays false and we move to the next location in the array until one of the procedures runs, and then we're done with that value.

pioName:= aValueFromArrayOrObject

done := false;

index:= 1

While (not done) Do

Begin

If array[index] = 0 then newValue

else IF array[index] = pioName then sameValue;

index := index+1;

End;

This could be a procedure in a ForEach statement. ForEach looks at every object in the drawing that matches your criteria, gets it's name and then runs above to either load the array or increment the counter in the array accordingly. With this approach you build one properly summarized array from the get go.

This *might* be a more efficient way, but if what you've got is working for you well... why rock the boat?

Link to comment

Hello,

Thank you so much for your comments.

Sorry it took so long to get back, but it has been one of those weeks. I hope to get back to this project this weekend and will take the posted information and evaluate it. It always inspiring to see how others code and to see if I am anywhere in the ballpark at times, especially with arrays and nested for loops.

Jeff Miller

Link to comment

Hi Jeff,

I actually haven't written anything meaningful in quite some time, and I'd like to thank you for posting and tweaking my interest again, and trying to see what you were up to.

If I may I'd make a couple more comments. I think you'll find that you're doing a few things in your code that don't need to be done. You don't need to have a separate "fill the first location" line. My example handles that in the first line of the loop. IE the first time we look at the array we can assume that it's blank. Actually, some might say that we need some error checking to make sure, but I've never done that and in some 8 years of using a tool daily that doesn't check I haven't had a failure.

Also, the section at your comment {IF NAMES DONT MATCH ADD 1 to arraycnt to look at next element} isn't needed if you increment the index after the logic is finished evaluating things. If neither condition is met, then the index increments to look at the next location in the array.

So let's say we're on the fifth instrument and it hasn't yet been entered into the array. The logic looks at each value in the array consecutively and doesn't find a match. It then encounters a blank location. That means that we've looked at every instrument in the array, so we add the value there. You can use the way an array is filled to your advantage.

The fact that I'm using 'done' to control the loop is essentially the same as what you're doing with: arrayCnt := kmaxArray; I've just rearranged things a bit.

On the subject of error checking: you may want to add a safety valve in case something funky starts happening. Something like:

While (not done) | (index < arrayCnt) Do....

If arrayCnt = kMaxArray then done := true; AlertMessage('funkiness encountered)';

I have something like that in a script, but it's never gotten funky. :-(

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...