TIP: Method to calculate a formula using c-object values

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

TIP: Method to calculate a formula using c-object values

4D Tech mailing list
Hi folks,
Here is a method I find incredibly useful. It's another one of those things
I suspect a lot of you have already written but I haven't seen anyone
talking about it so I will.

This method allows you to write a formula as a text string referring to
object keys then pass the formula and c-obj to the method. The key values
are inserted into the formula and the result is returned. Optionally you
can specify a result key and the result will be written to the object as
well.

​The method assumes you're using Canon's OBJ component. If not just
substitute whatever you use for getting/setting object values. This is v15
compatible. It could easily be modified to use the new dot-notation in v16.

Note that you must wrap key names in the backtic (`). I needed something to
identify a key from anything else and there are not a lot of unique options
on the keyboard anymore.

The actual calculation uses #4DEVAL so your formula can include:

key names

4D methods
project methods
process and IP variables

constants


A formula might look like this:  " ((`y` * 23) + `abs.frt`)^ `123.frt`"
The extra spaces are just for readability - they are ignored in execution.

All keys are read as REAL. Text keys are converted to real as are long and
boolean keys. Missing keys are zero.

If I want to assign the result of the formula to a key in the object:
 " `x`= ((`y` * 23)+`abs.frt`)^ `123.frt`"

If x doesn't exist it's created. If it does it's updated.

Finally the 3rd para allows you to specify writing the result as text in
the object. Why you do that? There are good reasons for managing all values
in a c-obj as text (it's easier). But this also allows you to do things
like date operations and save the result as a date string, for example.

Here are few uses:

​$formula:="`x` = Cos(`y`) "

$formula:="`x`=((`y`*23) + `abs.frt`) ^  `123.frt`"

$formula:="`x` = (<>IPconstant * processConstant)"  // referring to keys is
optional

$formula:="`newDate`=String(Add to date(Current
date;`year`;`month`;`day`);Internal date long)"


​This last one is particularly cool. ​It works because #4DEVAL simply
evaluates the string passed to it.

$formula:="<!--#4DEVAL "+$formula+"-->"  //calculation
PROCESS 4D TAGS($formula;$result)


The result is text even though the calculation may return a real (or any
other) value. Here's where the 3rd parameter comes in because it allows me
to write this text result to the object.

Consider an object:

{
"year": 0,
"month": 1,
"day": 23,
}

I pass the formula above:

$x:=OB_calculate ($obj;$formula;True)

​The object is updated to:

{
"year": 0,
"month": 1,
"day": 23,
"newDate": "March 16, 2018"
}​

​and $x is 162018​ which is simply the numbers of the date string and
meaningless.

I developed this method to manage listboxes where the data for the listbox
is in a hidden object array. This is a handy interface approach. It makes
it possible to display a listbox where the calculations involve a large
number of other values. Using arrays for the listbox data would typically
involve a lot of hidden arrays. Using an object array involves only the
columns of interest with the other data in the object.

With this method I can update the object array as required. And since the
formulas are all text based I don't even have to hard code the business
logic of the formulas. I could allow end users to define their own business
logic for their data, for instance. Or tailor how an interface works for
particular instances. You can do that now too, of course, but this is
probably a little easier.

Note on string objects. In v15 using

$real:=OB get($obj;"key";Is real)

doesn't correctly convert a text value if it contains commas. Using the Num
function accounts for this and also allows you to control the separator so
I suggest using:

$real:=Num(OB get($obj;"key";Is text))

​If the key is already a real this won't matter and if it's text it will be
handled correctly including currency symbols.​


Final note on key names - $pattern doesn't check for spaces in key names.
This is technically allowed but usually discouraged. If you need to
recognize key names with spaces tweak $pattern to suit. It does find
hyphenated keys, eg. "pad-right".

Be sure to add your preferred error checking code!

​=================================================​
 //  OB_calculate
  // Written by: Kirk as Designer, Created: 01/21/18, 11:17:23
  // ------------------
  // Method: OB_calculate (c-obj; text{;bool}) -> real
  // $1 is a c-obj
  // $2 is a formula
  //    ex:  ((`y`*23)+`abs.frt`)^`123.frt`
  //    or:  `x`= ((`y`*23)+`abs.frt`)^`123.frt  // write result to x
  // $3 is TRUE to write result to object as text  - default is false
  //    ignored if result isn't written to object
  // $0 is result
  // Purpose: The formula will be populated with key values and calculated
  // All keys are treated as REAL but the keys may be text, real, long,
bool or date
  // Missing keys = 0
  // Key names must be wrapped in backtic:  `thisKey`
​ and not contain spaces​
  // any formatting in the key values will be ignored
  // This can also be used to simply write a value to an object.
  // The result key, if specified, will be added if it's not there
  // negative numbers in the object must be specified as -1234
  //   can not user parens to indicate negatives
  // Formula may contain 4D expressions and process or IP vars

C_OBJECT($obj;$1)
C_TEXT($2;$formula)
C_BOOLEAN($3;$isText)
C_TEXT($resultKey)
C_REAL($0;$value)
C_LONGINT($i;$n)
C_TEXT($result;$pattern;$key;$str)

$obj:=OB_New
$obj:=$1

$formula:=$2
If (Count parameters>2)
$isText:=$3
End if

If (Position("=";$formula)>0)
$resultKey:=Substring($formula;1;Position("=";$formula)-1)
$resultKey:=Replace string($resultKey;"`";"")
$formula:=Substring($formula;Position("=";$formula)+1)
End if

  // --------------------------------------------------------
$pattern:="`([\\w\\d\\.
​-​
]+)`"  //  for finding keys
ARRAY LONGINT($aLen;0)
ARRAY LONGINT($aPos;0)

While (Match regex($pattern;$formula;1;$aPos;$aLen))
$key:=Substring($formula;$aPos{1};$aLen{1})

$value:=OBJ_Get_Real ($obj;$key)

$str:=Substring($formula;$aPos{0};$aLen{0})  // the entire match string

$formula:=Replace string($formula;$str;String($value))

End while

  // --------------------------------------------------------
$formula:="<!--#4DEVAL "+$formula+"-->"  //calculation
PROCESS 4D TAGS($formula;$result)

  // --------------------------------------------------------
$0:=Num($result)

Case of
: ($resultKey="")

: ($isText)
OBJ_Set_Text ($obj;$resultKey;$result)

Else
OBJ_Set_Real ($obj;$resultKey;Num($result))

End case


--
Kirk Brooks
San Francisco, CA
=======================

*​We go vote - they go home*
**********************************************************************
4D Internet Users Group (4D iNUG)
FAQ:  http://lists.4d.com/faqnug.html
Archive:  http://lists.4d.com/archives.html
Options: http://lists.4d.com/mailman/options/4d_tech
Unsub:  mailto:[hidden email]
**********************************************************************