WHO, WHAT and WHERE on wide and long queues

Simple example: let’s say I need a procedure to change the value of a specific field in a queue. WHO and WHAT works well. We use if often.

Except! It becomes quite slow with wide queues (lots of fields) and with the procedure being called repetitively for (say) 10 different fields (due to the WHO-loop trying to find the field position).

Is there a more efficient way of doing this?

SomeQueue QUEUE,TYPE
Field1      LONG
Field2      LONG
...etc...
Field50     LONG
          END



MyProcedure(STRING pFieldName,LONG pNewValue,*SomeQueue pQ)
i              long
lFieldPosition long
lFieldHolder   ANY

CODE
  lFieldPosition = 0
  loop 
    lFieldPosition +=1 
    if who(pQ,lFieldPosition)=pFieldName then break.
  end
  
  !No error checking above or  below to keep the example simple.

  lFieldHolder &= what(pQ,lFieldPosition)

  loop i=1 to records(pQ)
    get(pQ,i)
    lFieldHolder = pNewValue
    put(pQ)
  end

Could you possibly send a queue of items you want by name and their values. that way you are not processing the WHO list from 1 until the WHO finds something for every single field. If the fields you want in a 50 field group happen to be fields 49 and 50 you will loop through the fields 99 times. If you looped once and in the loop you used WHO to see if that field is in the list of queue items you want then you would only loop 50 times. On the 49th time you would check if the who is in the queue. If it is do you work. Loop to 50. Do the same thing.

Jeff

I am being very generic in my code but something like this

MyProcedure(*someQueueA pFieldAndValue,*SomeQueueB pQ)
i              long
lFieldPosition long

CODE
  LOOP lFieldPosition = 1 TO MyGrpRef{PROP:Fields}
    IsInQueue = FALSE
    LocValue = who(pQ,lFieldPosition)
    DO FindInQueue
    IF IsInQueue
      DO AssignValue
    END
    !You could break out here if all fields in the queue have been found.
  end

Another option is to change this logic to a class where you can pass in the queue reference.
Then loop through the queue one time and build an internal queue of all the fields and their names.
Then when you call the method to set the field value, you can use the queue of fields to go straight to the field without having to use WHO again to find the column

1 Like

The answer is complicated by the fact that you havent said much about the context. But given that you’re asking about performance, I’m guessing you are calling this procedure a lot, perhaps on the same queue or queue type.

If that is the case the Reflection Class will likely help. This reflects the structure once, and after that setting, or getting, values in structure gets much faster.

[I’ll post example code in the morning.]

Something like (not tested);

ref  ReflectClass
  code
  ref.start()
  ref.parse('queue', SomeQueue)
  MyProcedure(ref,'somename','somevalue',SomeQueue)
  
MyProcedure(ReflectClass pReflection, STRING pFieldName,LONG pNewValue,*SomeQueue pQ)
groupname   cstring(100)
fieldname   cstring(100)
lposition    long
  code
  groupname = 'queue'
  fieldname = clip(pFiedname)
  lFieldPosition = ref.GetNumber(groupname,fieldname)
...
1 Like

How many types of queues do you have to pass to this procedure? Maybe it would speed things up to have some overloaded procedures with queue types?

Then the overloaded procedure could call another method with the specific fields by address, to process the queue.

This could help solve your horizontal loop slowness.

2 Likes

Jup. Agreed - that is one way of doing it.

I was just wondering if there is not some better way than LOOP-ing and WHO-ing.

Thanks. Did I miss the example code?

I guess in one way the question is: One can say “where(pQ,pQ.Field44)”, but one cannot say “where(pQ,“Field44”)”.

So that brings one to the point of having to do this whole LOOPING-ing and WHO-ing thing.

Is there nothing simpler?

If you’re not changing which fields you’re assigning each time through the loop
Then just find them ONCE above the loop

You might want to use something like the FieldPairs class that ABC uses.

Any easy generic approach to this, Mark?

There are occasions when I need to automate the importing of delimited text files into SQL.
It’s easy enough to hard-code based on column order and column name, but because the Universe hates me sometimes people will rearrange columns.
So I’ve also hard-coded a two step process to search the header row for column names, then map the column order to my SQL insert statements.

So my question is whether there’s an easier generic approach to this?

1 Like

I’ve added the code now.

1 Like

Had a look at the Reflection class to see if it uses some “magical” way to find a field position - i,.e. without having to traverse the whole queue structure and WHO-ing.

But it seems to me it does exactly that (.GetFieldNumber goes into a LOOP, as does .Parse).

So Reflection also uses the strategy of “understanding” (parsing) the queue beforehand, and then using that info for faster subsequent processing.

And of course - that’s OK (we also often do that in our code).

I was just wondering if there is something simpler (more direct). Seems not.