In this question i asked how to perform a conditional increment. The provided answer worked, but does not scale well on huge data-sets.
The Input:
<Users>
<User>
<id>1</id>
<username>jack</username>
</User>
<User>
<id>2</id>
<username>bob</username>
</User>
<User>
<id>3</id>
<username>bob</username>
</User>
<User>
<id>4</id>
<username>jack</username>
</User>
</Users>
The desired output (in optimal time-complexity):
<Users>
<User>
<id>1</id>
<username>jack01</username>
</User>
<User>
<id>2</id>
<username>bob01</username>
</User>
<User>
<id>3</id>
<username>bob02</username>
</User>
<User>
<id>4</id>
<username>jack02</username>
</User>
</Users>
For this purpose it would be nice to
- sort input by username
- for each user
- when previous username is equals current username
- increment counter and
- set username to '$username$counter'
- otherwise
- set counter to 1
- when previous username is equals current username
- (sort by id again - no requirement)
Any thoughts?
This is kind of ugly and I'm not fond of using
xsl:for-each
, but it should be faster than using preceding-siblings, and doesn't need a 2-pass approach:If you really need it sorted by ID afterwards, you can wrap it into a two-pass template:
Here's a slight variation, but possible not a great increase in efficiency
What this is doing is defining a key to group Users by username. Then, for each username element, you look through the elements in the key for that username, and output the position when you find a match.
One slight advantage of this approach is that you are only looking at user records with the same name. This may be more efficient if you don't have huge numbers of the same name.
This transformation produces exactly the specified wanted result and is efficient (O(N)):
When applied on the provided XML document:
the wanted, correct result is produced: