Search code examples
javamuledataweavemule4

Derive parent-child hierarchy from an array using DataWeave (additional details)


This is a follow up to an earlier question. Thanks @harshank for the quick help. I am posting this question separately since there was an additional check required which I had not asked in the earlier question. So, the solution posted earlier works fine for the stated requirement.

I am adding the additional requirement here:

  1. The decision if gp and parent match depends on an additional attribute eye_colour (please see sample payload below).

  2. Also in the response additional fields need to be populated (which was not mentioned in the earlier question). Things like name and eye_colour.

So, as an example this is the payload:

[
  {
    "gp": "T1",
    "gp_eye_colour": "blue",
    "gp_name" : "John",
    "parent": "T1",
    "parent_eye_colour" : "black",
    "parent_name" : "Sam",
    "child": "C1",
    "child_eye_colour" : "brown",
    "child_name" : "C1"

  },
 {
    "gp": "T1",
    "gp_eye_colour": "blue",
    "gp_name" : "John",
    "parent": "T1",
    "parent_eye_colour" : "black",
    "parent_name" : "Sam",
    "child": "C1",
    "child_eye_colour" : "brown",
    "child_name" : "C1"

  }
  
]

And now using the earlier solution, I have tried to build on top of it as:

%dw 2.0
output application/xml
// hierarchy's structure. If, lets say you add a "grand child" it should work. (haven't tested this though)
var hierarchy = {
    gp: {
        parent: {
            child: null
        }
    }
}
fun getDirectGeanologies(records, hierarchyStructure,isGp,isParent,isChild) = do {
    var topLevel = keysOf(hierarchyStructure)[0] as String
    var secondLevel = keysOf(hierarchyStructure[topLevel])[0]
    ---
    (records groupBy $[topLevel])
        mapObject ((level1Childs, code) -> 
            genealogy: {
                code: code,
                eye_colour: if(isGp) (level1Childs.gp_eye_colour) else if(isParent) (level1Childs.parent_eye_colour) else (level1Childs.child_eye_colour),
                name: if(isGp) (level1Childs.gp_name) else if(isParent) (level1Childs.parent_name) else (level1Childs.child_name),
                hierarchy:
                    if(secondLevel != null) // If there are no more childs, do nothing.
                        (level1Childs groupBy ((item) -> item[secondLevel] ~= code) // This is to handle when parent = gp
                            mapObject ((value, hasSameParent) -> 
                                // If parent = gp, we want to skip parent and link directly to child
                                if(hasSameParent as Boolean) getDirectGeanologies(value, hierarchyStructure[topLevel][secondLevel],false,false,true) 
                                // recurrsively call the function with child records and going down the hierarchyStructure by one
                                else getDirectGeanologies(value, hierarchyStructure[topLevel],false,true,false) 
                        ))
                    else {}
            }
        )
}

---
list: getDirectGeanologies(payload,hierarchy,true,false,false)

However, it is generating duplicate elements for eye_colour and name. Ideally, it should print the values only once with the value of either gp / parent / child.

This is the expected response:

<?xml version='1.0' encoding='UTF-8'?>
<list>
  <genealogy>
    <code>T1</code>
    <eye_colour>blue</eye_colour>
    <name>John</name>
    <hierarchy>
      <genealogy>
        <code>C1</code>
        <eye_colour>brown</eye_colour>
        <name>C1</name>
        <hierarchy/>
      </genealogy>
    </hierarchy>
  </genealogy>
</list>

I was also not able to figure out how to check if gp and parent have same values based on two fields (gp & gp_eye_colour vs parent & parent_eye_colour)

Thank you @sudhish_s for the response, adding an example to elaborate regarding decision of when to skip parent (when gp & gp_eye_colour of gp are same as parent & parent_eye_colour of parent).

Sample input: (Here parent Sam should not be skipped since though gp and parent are same their eye colours are different. Similarly, in case of Don, this parent should be skipped since gp and parent and their eye colours are the same).

[
  {
    "gp": "T1",
    "gp_eye_colour": "blue",
    "gp_name" : "John",
    "parent": "T1",
    "parent_eye_colour" : "black",
    "parent_name" : "Sam",
    "child": "C1",
    "child_eye_colour" : "brown",
    "child_name" : "C1"

  },
 {
    "gp": "T1",
    "gp_eye_colour": "blue",
    "gp_name" : "John",
    "parent": "T1",
    "parent_eye_colour" : "blue",
    "parent_name" : "Don",
    "child": "C1",
    "child_eye_colour" : "brown",
    "child_name" : "C1"
  }  
]

Here is the expected response:

<?xml version='1.0' encoding='UTF-8'?>
<list>
  <genealogy>
    <code>T1</code>
    <eye_colour>blue</eye_colour>
    <name>John</name>
    <hierarchy>
      <genealogy>
        <code>T1</code>
        <eye_colour>black</eye_colour>
        <name>Sam</name>
        <hierarchy>
          <genealogy>
            <code>C1</code>
            <eye_colour>brown</eye_colour>
            <name>C1</name>
            <hierarchy/>
          </genealogy>
        </hierarchy>
      </genealogy>
      <genealogy>
        <code>C1</code>
        <eye_colour>brown</eye_colour>
        <name>C1</name>
        <hierarchy/>
      </genealogy>
    </hierarchy>
  </genealogy>
</list>

Solution

  • Slightly different version. Used arrays for reference.

    %dw 2.0
    output application/xml
    var hierarchy = ["gp", "parent", "child"]
    fun getDirectGeanologies(records, level) = do {
        var hLevel = hierarchy[level]
        ---
        records groupBy $[hLevel] mapObject ((children, code) -> 
            genealogy: {
                code: code,
                eye_colour: children[0][hLevel ++ "_eye_colour"],
                name: children[0][hLevel ++ "_name"],
                hierarchy: 
                    if (level == sizeOf(hierarchy) - 1) {}
                    else do {
                        var nextLevel = level + 1
                        var nextGen = children groupBy ($[hierarchy[nextLevel]])
                        --- 
                        nextGen mapObject ((nextGenChildren, nextGenCode) -> 
                            if (nextGenCode == code)
                                getDirectGeanologies (nextGenChildren, nextLevel + 1 )
                            else
                                getDirectGeanologies (nextGenChildren, nextLevel)
                        )                   
                    }
                                                
            }
        )
    }
    ---
    list: getDirectGeanologies(payload,0)
    
    

    The issue with elements appear multiple times is due to the level1Childs is Array. Change the output to json, it will be more clear. If you want to use your code, change the eye_colour and name mapping to below

                    eye_colour: if(isGp) (level1Childs[0].gp_eye_colour) else if(isParent) (level1Childs[0].parent_eye_colour) else (level1Childs[0].child_eye_colour),
                    name: if(isGp) (level1Childs[0].gp_name) else if(isParent) (level1Childs[0].parent_name) else (level1Childs[0].child_name),