Thursday, September 23, 2010

Tutorial: Undocumented behavior of kernel functions min()/max()

I was recently reviewing some code written to be shipped with AX6, and noticed an unfamiliar pattern being used in it. I investigated a bit deeper, and turns out it actually works fine on previous versions of AX as well.


I am talking about 2 kernel functions for finding the maximum or minimum of the specified values.
The signature of these methods is shown on the below image:


As you can see, it takes 2 arguments of anytype, and returns an anytype which is the largest of the two values. But it can accept much more than 2 arguments, even though it is not documented as such.

I wrote a small job to showcase this behavior. The code is provided below. You can also download it from my SkyDrive Dynamics AX share.

static void Tutorial_MinMaxFunctions(Args _args)
{
    #define.ArraySize(11)

    Random  rand = new Random();

    int     counter;
    int     arrayInt[#ArraySize];
    str     arrayIntAsString;
    int     arrayIntMaxValue;
    int     arrayIntMinValue;
    ;

    for (counter = 1; counter <= #ArraySize; counter++)
    {
        arrayInt[counter] = rand.nextInt();
        if (arrayIntAsString)
            arrayIntAsString += ', ';
        arrayIntAsString += int2str(arrayInt[counter]);
    }
    info("Generated array of integers: " + arrayIntAsString);

    info("The typical way to find a maximum is by looping through all the values one by one, calling the comparison function multiple times");
    arrayIntMaxValue = minint();
    arrayIntMinValue = maxint();
    for (counter = 1; counter <= #ArraySize; counter++)
    {
        arrayIntMaxValue = max(arrayIntMaxValue, arrayInt[counter]);
        arrayIntMinValue = min(arrayIntMinValue, arrayInt[counter]);
    }
    info(strfmt("Max.value: %1 and Min.value: %2", int2str(arrayIntMaxValue), int2str(arrayIntMinValue)));

    info("Using max and min with 11 arguments works just as well");
    arrayIntMaxValue = minint();
    arrayIntMinValue = maxint();
    arrayIntMaxValue = max(arrayInt[1], arrayInt[2], arrayInt[3], arrayInt[4], arrayInt[5], arrayInt[6], arrayInt[7], arrayInt[8], arrayInt[9], arrayInt[10], arrayInt[11]);
    arrayIntMinValue = min(arrayInt[1], arrayInt[2], arrayInt[3], arrayInt[4], arrayInt[5], arrayInt[6], arrayInt[7], arrayInt[8], arrayInt[9], arrayInt[10], arrayInt[11]);
    info(strfmt("Max.value: %1 and Min.value: %2", int2str(arrayIntMaxValue), int2str(arrayIntMinValue)));

    info("Note that comparing an integer and a real also works, as well as outputing the results straight into an infolog message");
    info(max(12, 12.001));
}


Another interesting point is that it can actually accept different types of arguments, for example, a real and an integer, as shown above. And it actually returns an anytype, which implicitly gets converted to a string when sent to the infolog.

Disclaimer: Since this is not a documented feature, it can theoretically change in the future releases, but I doubt it in this particular case.

4 comments:

  1. hi Ivan,

    i have created one method as shown below,


    void FilterProductionOrder(str 20 _prodPoolDate,

    str 10 _prodPoolId, boolean update = false)

    {







    Query query = new Query();

    QueryBuildDataSource qbdsprodTable = query.addDataSource(tableNum(ProdTable));

    QueryRun queryRun;



    ;



    info("Date: " + _prodPoolDate + "PoolName: "+ _prodPoolId);



    qbdsprodTable.addRange(fieldNum(ProdTable,SchedDate)).value(queryValue(_ProdPoolDate));

    queryRun = new QueryRun(query);





    startLengthyOperation();

    while (queryRun.next())

    {

    prodTable = queryRun.getNo(1);

    info (strFmt("PoolId : %1 SchDate: %2", prodTable.ProdPoolId,

    prodTable.SchedDate));

    ttsBegin;

    prodTable.ProdPoolId = _prodPoolId;

    ttscommit;

    }

    endLengthyOperation();

    }



    here i want to update Production Pool as i have written,

    ttsBegin;

    prodTable.ProdPoolId = _prodPoolId;

    ttscommit;

    MY AIM IS " while fetching record which has date mention in "_prodPoolDate", that all should be assigned to Production pool mention in "_prodPoolId" "

    Above code is executing without error. but not gives desired result.

    Pls. replay as soon as possible.



    Thank You in Advance.



    Regards,

    Mehul Thacker

    ReplyDelete
  2. hi mehul here please do replay as soon as possible, once again thank you.
    you can contact me on mehul@windowmaker.com

    ReplyDelete
  3. Thank you for replay, i have read your mail,
    i do implement as you mention. temporary i overcome to this problem by modifying above code by:

    ttsBegin;
    -->> prodTable.SelectForUpdate(true); (This line added)
    prodTable.ProdPoolId = _prodPoolId;
    -->> prodTable.update();(This line added)
    ttscommit;

    ReplyDelete
  4. Hi Ivan,

    I need to find out the value of a purchase order that includes line amount + only those misc charges that have load on inventory as non zero.

    Pls help me in this regard as this is quite urgent

    ReplyDelete

Please don't forget to leave your contact details if you expect a reply from me. Thank you