BizTalk Mapper - Keyed Cumulative Sum

Listen with webReader
Published 02 May 11 11:52 PM | Johan Hedberg

From time to time I get questions about mapping puzzles, and I love it! Send me more of those! (I realize might regret that statement later). This time the puzzle was how best to do a conditional cumulative sum, or keyed conditional sum, also possibly known as grouped conditional sum.

The scenario is this (I’ve removed some namespaces etc for readability):

<Compensation>
  <rows>
    <row>
      <Compensation_Code>1</Compensation_Code>
      <Compensation_SubCode>1</Compensation_SubCode>
      <Compensation_Level>1</Compensation_Level>
      <Compensation_Id>1113</Compensation_Id>
      <Compensation_Days>41856.50</Compensation_Days>
      <Compensation_Amount>25288084</Compensation_Amount>
      <Compensation_Tax>6690289</Compensation_Tax>
    </row>
  <row>
      <Compensation_Code>1</Compensation_Code>
      <Compensation_SubCode>1</Compensation_SubCode>
      <Compensation_Level>1</Compensation_Level>
      <Compensation_Id>1113</Compensation_Id>
      <Compensation_Days>41856.50</Compensation_Days>
      <Compensation_Amount>29627</Compensation_Amount>
      <Compensation_Tax>6690289</Compensation_Tax>
    </row>
    <row>
      <Compensation_Code>1</Compensation_Code>
      <Compensation_SubCode>1</Compensation_SubCode>
      <Compensation_Level>2</Compensation_Level>
      <Compensation_Id>1113</Compensation_Id>
      <Compensation_Days>41856.50</Compensation_Days>
      <Compensation_Amount>234348</Compensation_Amount>
      <Compensation_Tax>6690289</Compensation_Tax>
    </row>
  </rows>
</Compensation>

How do you best produce a map that as step one summarize all Compensation_Amount for each variation of Compensation_SubCode, and returns this (let’s call this scenario 1):

<Compensation>
  <rows>
    <row>
      <Compensation_SubCode>1</Compensation_SubCode>
      <Compensation_Amount>25552059</Compensation_Amount>
    </row>
  </rows>
</Compensation>

and as step two, summarize all Compensation_Amount for each combination of Compensation_SubCode and Compensation_Level, and return this (let’s call this scenario 2):

<Compensation>
  <rows>
    <row>
      <Compensation_SubCode>1</Compensation_SubCode>
      <Compensation_Level>1</Compensation_Level>
      <Compensation_Amount>25317711</Compensation_Amount>
    </row>
    <row>
      <Compensation_SubCode>1</Compensation_SubCode>
      <Compensation_Level>2</Compensation_Level>
      <Compensation_Amount>234348</Compensation_Amount>
    </row>
  </rows>
</Compensation>

You might start thinking custom xslt and xsl:sort or xsl:keys or muenchian methods and things like that. That’s one way to do it. I actually prefer using the mapper for most maps until performance forces me to go another way.

My solution for scenario 1:

clip_image001

My Solution for scenario 2:

clip_image002

I am not presenting a complete solution for scenario 1, since it’s really just a subset of the functionality required for scenario 2, but you should get the point from the explanation of scenario 2.

The magic of the scenario 2 solution happens in the three scripting functoids. The first one, connected to the destination root node contains the following snippet (again namespace deprived for readability):

List<string> keyList= new List<string>();
void initglobals() {}

It’s simply responsible for making sure that the variable keyList is set up. The empty method that return void ensures that I can connect this to an output node to make sure it triggers, yet it will produce no output.

The next functoid then makes use of that list, and adds all unique keys to it (which is unique combinations of the two keys separated by a secure character, in this case a colon):

public string AddKey(string key)
{
  if (keyList.Contains(key))
    return string.Empty;
  keyList.Add(key);
  return key;
}

The Not Equals functoid then makes sure that we will only see output when a new unique combination is found, by checking to make sure that the string outputted from the scripting functoids <> and empty string.

The third functoid then is responsible for doing the summation (I’ve inserted a linebreak and some spaces in the xpath statement for readability, so don’t just copy paste this):

<xsl:template name="OutputSum">
  <xsl:param name="param1" />
  <xsl:param name="param2" />
  <xsl:element name="Compensation_Amount">
  <xsl:value-of select="sum(//row[Compensation_SubCode=$param1 
              and Compensation_Level=$param2]/Compensation_Amount)" />
  </xsl:element>
</xsl:template>

This simple xslt call template takes the two key values in, and does a summation of all rows that has these key values set to these specific values.

Now someone might object and say that this would mean an unnecessary amount of summation gets done. But that’s actually not the case, the mapper is clever enough to only do this under the condition of the Not Equals functoid returning true, as the following xslt snippet shows (this is just a portion of the xslt generated):

<xsl:variable name="var:v3" select="userCSharp:LogicalNe(string($var:v2) , &quot;&quot;)" />
<xsl:if test="$var:v3">
  <row>
    ...
    <xsl:call-template name="OutputSum">
    <xsl:with-param name="param1" select="string(Compensation_SubCode/text())" />
    <xsl:with-param name="param2" select="string(Compensation_Level/text())" />
    </xsl:call-template>
  </row>
</xsl:if>
’So a short summation (pun intended) of the work involved:

1. Create a global variable, a list to hold keys

2. Add to that list once you discover a new key

3. Add logic to that discovery to only output your destination node when you hit a new key

4. Implement a xslt call template and use xpath to sum based on your keys

Filed under:

Comments

# Ravindar Thati said on July 1, 2011 09:09 PM:

Hey Johan, all your posts are pretty good.

And regarding mapping, i am not using scripting functiods as it is tough for me.

To me, using xslt is easy.

i feel it tough to use the scripting functiods.

i write xslt easily what i want.

but looking at your post i felt i can put my hands in scripting functiods as well. thank you. :)

will there be any performance degrade if we use xslt?

if yes how?

in this example you explained, do u perfer to use mapping in mapper or xslt?

# Matt Milner said on December 20, 2011 05:27 PM:

Johan, great post with an elegant solution. I was going to do purely XSLT, but this make the XSLT minimal, which was important for me as I have to hand this off to a client. Thanks for the great post!

# Sivaram said on January 13, 2012 02:07 PM:

This was very much helpful... Its simply great..

Leave a Comment

(required) 
(required) 
(optional)
(required) 

This Blog

News

    Messenger

    Twitter Updates

      Follow me on twitter

      Visitors

      Feedburner Subscribers

      Locations of visitors to this page

      Disclaimer

      All material is provided AS IS voiding any thinkable or unthinkable effect it might have for any use whatsoever. There... is that clear enough ;)

      Pages

    Syndication