Pages

Thursday, May 12, 2016

A dynamic strFmt call

In the communities a users asked about a way to make a dynamic strFmt call, not knowing the number of parameters up front.

With some good critical tests from Levgen Miroshnikov (blog) I think I have managed to write a class method that does that.

Regular StrFmt will round decimal value and change the decimal point to whatever fits your windows locale. This method has a parameter allowing you to keep the formatting of decimal values.

Here is the code for it:
public str dynamicStrFmt(
    str         _expression,
    container   _args = conNull(),
    boolean     _keepRealsDecimals = false)
{
    str         ret;
    Counter     counter;
    str         argStr;
    char        argChar;

    if (_args == conNull())
    {
        return strFmt(_expression);
    }

    for (counter = 1; counter <= strLen(_expression); counter++)
    {
        if (subStr(_expression, counter, 1) != '%')
        {
            ret += subStr(_expression, counter, 1);
        }
        else
        {
            // This is a point in the string where an argument starts
            // Find the end of the argument
            argStr  = '';
            argChar = subStr(_expression, counter + 1, 1);
            while (argChar && argChar == strKeep(argChar, '0123456789'))
            {
                argStr += argChar;
                argChar = subStr(_expression, counter + strLen(argStr) + 1, 1);
            }

            // Lookup and insert the value to the result
            if (argStr)
            {
                // Check that the argument is within the bounds of the number of supplied argument values
                if (str2Int(argStr) <= conLen(_args)) 
                {
                    if (typeOf(conPeek(_args, str2Int(argStr))) == Types::Real && _keepRealsDecimals)
                    {
                        ret += num2str(conPeek(_args, str2Int(argStr)), -1, numOfDec(conPeek(_args, str2Int(argStr))), 1, 0);
                    }
                    else
                    {
                        ret += strFmt('%1', conPeek(_args, str2Int(argStr)));
                    }
                }
                else
                {
                    // The argument is not within the bounds of the supplied argument values. Keep the argument unchanged.
                    ret += subStr(_expression, counter, 1) + argStr;
                }
                // Move the current position in the _expression string to a position after the argument
                counter += strLen(argStr);
            }
            else
            {
                // Didn't find the argument to be meningfull, so just add the argument character as-is
                ret += subStr(_expression, counter, 1);
            }
        }
    }

    return ret;
}

And a main method for a quick demo:
public static void main(Args _args)
{
    aaaStrFmtBuilder    demo = new aaaStrFmtBuilder();

    info (demo.dynamicStrFmt("%1 + %1 = %2", [2, 4]));
    info(demo.dynamicStrFmt("%1/(%2*%1)", [1000, 0.135], true));
    info(demo.dynamicStrFmt("%2 %3 %1", ['AX', 'Microsoft', 'Dynamics']));
}

And here you can download a project with the class and unit tests for it:
 

The two classes are prefixed with 'aaa', making it easier for you to find them in the AOT :-)
Please change the names if you are going to use the code for something real.

No comments: