Search code examples
muledataweavemulesoftanypoint-studiomule4

How to sum the values of child objects in an array with DataWeave


Below is the input. There is a parent child-relationship with objects. The "Key" key specifies that. For e.g. 109-200C-001 is the child of 109-200C. There will be n number of objects with different strings.

[
    {
        "Class": 1,
        "Amount": 2,
        "Key": "109-200C-001"
    },
    {
        "Class": 3,
        "Amount": 4,
        "Key": "109-200C"
    },
    {
        "Class": 8,
        "Amount": 7,
        "Key": "xyz-123-007"
    },
    {
        "Class": 8,
        "Amount": 4,
        "Key": "xyz-123"
    },
]

Need the output as below: Only the parent objects with the Amount of Child and Parent added.

[
    
    {
        "Class": 3,
        "Amount": 6,
        "Key": "109-200C"
    },
    {
        "Class": 8,
        "Amount": 11,
        "Key": "xyz-123"
    }
]

Solution

  • I first remove the child objects, then for each parent I sum the values of all elements that start with the same key. Encapsulating part of the logic in functions make the script very declarative in intent.

    %dw 2.0
    output application/json
    import * from dw::core::Arrays
    fun isParent(a, x)=a some ( !($.Key == x.Key) and ($.Key startsWith x.Key))
    fun sumChilds(a, x)=a filter  ($.Key startsWith x.Key) sumBy $.Amount
    ---
    payload 
        filter isParent(payload, $)
        map {
            Class: $.Class,
            Ammount: sumChilds(payload, $),
            Key: $.Key
        }
    

    Output:

    [
      {
        "Class": 3,
        "Ammount": 6,
        "Key": "109-200C"
      },
      {
        "Class": 8,
        "Ammount": 11,
        "Key": "xyz-123"
      }
    ]
    

    I could have used the update operator instead of map().

    Find below generalized solution that works passing the keys as parameters:

    %dw 2.0
    output application/json
    import * from dw::core::Arrays
    
    fun isParent(a, x, key)=a some ( !($[key] == x[key]) and ($[key] startsWith x[key]))
    
    fun sumChilds(a, x, key, valueKey)=a filter ($[key] startsWith x[key]) sumBy ($[valueKey] as Number)
    
    fun totalizeChilds(a, key, valueKey)=
        a 
            filter isParent(a, $, key)
            map ((item, index) ->  item update {
                    case v at ."$(valueKey)" -> sumChilds(payload, item, key, valueKey)
                }
            )
    ---
    totalizeChilds(payload, "LOCAL_CODE", "AMOUNT")
    

    For your second input you would need to map AMOUNT to string if you want an exact match.