Skip to main content
niranjanreddym0001
August 16, 2021
Solved

Expression Rule

  • August 16, 2021
  • 6 replies
  • 0 views

Hi,

I have a below output from my expression

My requirement is if "expr" of every Dictionary contains "rowcode" that row code needs to be replaced by "expr" of that particular rowcode (for eg: "(FRML_88_1+FRML_88_2)" this expression contains rowcode so i need to replace it with expr "((123/12)+(1284*64))" and  the final output be like  "(((123/12)+(1284*64))*(9090+122))"

    Best answer by csteward

    I digested this a little bit, and it appears that "expr" is either a mathematical expression or a reference to another expression based on the rowcode, so the rowcode should be replaced into that expression when it exists in the expr.

    Great case for a recursive function here.  This example assumes there are always 2 objects within the "expr" separated by +,-,* or /, and the rowcode references always contain the text "FRML".

    Additionally, to assist I've created a new local!data2 variable which breaks out the expr into 2 separate parts and operator, easier to parse that way.

    Primary, rule!chris_21640():

    a!localVariables(
      local!data: {
        a!map(expr: "(123/12)", rowcode: "FRML_88_1"),
        a!map(expr: "(1284*64)", rowcode: "FRML_88_2"),
        a!map(expr: "(FRML_88_1+FRML_88_2)", rowcode: "FRML_88_3"),
        a!map(expr: "(9090+122)", rowcode: "FRML_88_4"),
        a!map(expr: "(FRML_88_3*FRML_88_4)", rowcode: "FRML_88_5")
      },
      local!operators: {"+","-","*","/"},
      local!data2: a!flatten(
        a!forEach(
          items: local!data,
          expression: {
            a!localVariables(
              local!row: fv!item.expr,
              local!rowcode: fv!item.rowcode,
              reject(
                fn!isnull,
                a!flatten(
                  a!forEach(
                    items: local!operators,
                    expression: if(
                      search(fv!item,local!row)>0,
                      a!localVariables(
                        local!operator: fv!item,
                        local!items: split(stripwith(local!row,"()"),fv!item),
                        a!map(
                          expr1: local!items[1],
                          expr2: local!items[2],
                          operator: local!operator,
                          rowcode: local!rowcode
                        )
                      ),
                      null
                    )
                  )
                )
              )
            )
          }
        )
      ),
      
      a!forEach(
        items: local!data2,
        expression: rule!chris_21640_recur(
          data: local!data2,
          expr1: fv!item.expr1,
          expr2: fv!item.expr2,
          operator: fv!item.operator
        )
      )
    )

    And the helper recursive rule, rule!chris_21640_recur():

    concat(
      "(",
      if(
        search("FRML",ri!expr1)>0,
        a!localVariables(
          local!index: wherecontains(ri!expr1,ri!data.rowcode),
          rule!chris_21640_recur(
            data: ri!data,
            expr1: index(ri!data.expr1,local!index,null),
            expr2: index(ri!data.expr2,local!index,null),
            operator: index(ri!data.operator,local!index,null),
          )
        ),
        ri!expr1,
      ),
      ri!operator,
      if(
        search("FRML",ri!expr2)>0,
        a!localVariables(
          local!index: wherecontains(ri!expr2,ri!data.rowcode),
          rule!chris_21640_recur(
            data: ri!data,
            expr1: index(ri!data.expr1,local!index,null),
            expr2: index(ri!data.expr2,local!index,null),
            operator: index(ri!data.operator,local!index,null),
          )
        ),
        ri!expr2,
      ),
      ")"
    )

    This will return a list of mathematical expressions, replacing (recursively) rowcode references within the expressions. 

    6 replies

    mikes0011
    Brainy
    August 16, 2021

    Can you clarify what you're trying to do here?  I've read this a few times now and I can't quite figure out what ```if "expr" of every Dictionary contains "rowcode"``` means.  From your screenshot it looks like every entry contains both "rowcode" and "expr" properties.

    csteward
    cstewardAnswer
    August 16, 2021

    I digested this a little bit, and it appears that "expr" is either a mathematical expression or a reference to another expression based on the rowcode, so the rowcode should be replaced into that expression when it exists in the expr.

    Great case for a recursive function here.  This example assumes there are always 2 objects within the "expr" separated by +,-,* or /, and the rowcode references always contain the text "FRML".

    Additionally, to assist I've created a new local!data2 variable which breaks out the expr into 2 separate parts and operator, easier to parse that way.

    Primary, rule!chris_21640():

    a!localVariables(
      local!data: {
        a!map(expr: "(123/12)", rowcode: "FRML_88_1"),
        a!map(expr: "(1284*64)", rowcode: "FRML_88_2"),
        a!map(expr: "(FRML_88_1+FRML_88_2)", rowcode: "FRML_88_3"),
        a!map(expr: "(9090+122)", rowcode: "FRML_88_4"),
        a!map(expr: "(FRML_88_3*FRML_88_4)", rowcode: "FRML_88_5")
      },
      local!operators: {"+","-","*","/"},
      local!data2: a!flatten(
        a!forEach(
          items: local!data,
          expression: {
            a!localVariables(
              local!row: fv!item.expr,
              local!rowcode: fv!item.rowcode,
              reject(
                fn!isnull,
                a!flatten(
                  a!forEach(
                    items: local!operators,
                    expression: if(
                      search(fv!item,local!row)>0,
                      a!localVariables(
                        local!operator: fv!item,
                        local!items: split(stripwith(local!row,"()"),fv!item),
                        a!map(
                          expr1: local!items[1],
                          expr2: local!items[2],
                          operator: local!operator,
                          rowcode: local!rowcode
                        )
                      ),
                      null
                    )
                  )
                )
              )
            )
          }
        )
      ),
      
      a!forEach(
        items: local!data2,
        expression: rule!chris_21640_recur(
          data: local!data2,
          expr1: fv!item.expr1,
          expr2: fv!item.expr2,
          operator: fv!item.operator
        )
      )
    )

    And the helper recursive rule, rule!chris_21640_recur():

    concat(
      "(",
      if(
        search("FRML",ri!expr1)>0,
        a!localVariables(
          local!index: wherecontains(ri!expr1,ri!data.rowcode),
          rule!chris_21640_recur(
            data: ri!data,
            expr1: index(ri!data.expr1,local!index,null),
            expr2: index(ri!data.expr2,local!index,null),
            operator: index(ri!data.operator,local!index,null),
          )
        ),
        ri!expr1,
      ),
      ri!operator,
      if(
        search("FRML",ri!expr2)>0,
        a!localVariables(
          local!index: wherecontains(ri!expr2,ri!data.rowcode),
          rule!chris_21640_recur(
            data: ri!data,
            expr1: index(ri!data.expr1,local!index,null),
            expr2: index(ri!data.expr2,local!index,null),
            operator: index(ri!data.operator,local!index,null),
          )
        ),
        ri!expr2,
      ),
      ")"
    )

    This will return a list of mathematical expressions, replacing (recursively) rowcode references within the expressions. 

    peter.lewis
    Employee
    August 16, 2021

    This is a fun problem! I think you could simplify it by assuming that the operators don't need to be specified individually, since each of the sub-expressions are valid on their own. If you do, then you can do a more straightforward substitute(). Also, this is one of the few examples where the reduce() function actually makes this easier! The important thing here is that the result of the previous must be used for the next loop (since you'd need that to use the result of FRML_88_3 in the last item). Here's what I did:

    Main Rule

    a!localVariables(
      local!data: {
        a!map(
          expr: "(123/12)",
          rowcode: "FRML_88_1"
        ),
        a!map(
          expr: "(1284*864)",
          rowcode: "FRML_88_2"
        ),
        a!map(
          expr: "(FRML_88_1+FRML_88_2)",
          rowcode: "FRML_88_3"
        ),
        a!map(
          expr: "(9090+122)",
          rowcode: "FRML_88_4"
        ),
        a!map(
          expr: "(FRML_88_3*FRML_88_4)",
          rowcode: "FRML_88_5"
        )
      },
      reduce(
        rule!PDL_RecursiveSupportingRule,
        local!data,
        enumerate(length(local!data)) + 1,
      )
    )

    Supporting Rule

    a!forEach(
      items: ri!data,
      expression: if(
        search(
          index(ri!data.rowcode, ri!index, {}),
          fv!item.expr
        ) > 0,
        a!update(
          data: fv!item,
          index: "expr",
          value: substitute(
            fv!item.expr,
            index(ri!data.rowcode, ri!index, {}),
            index(ri!data.expr, ri!index, {}),     
          ),
        ),
        fv!item
      )
    )

    csteward
    August 16, 2021

    Nice code, much cleaner!  Would love to runs some tests cases with it but alas, we are only on 21.1 (no a!update() yet)!