Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 256765
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 11, 20262026-05-11T22:02:28+00:00 2026-05-11T22:02:28+00:00

OK, I KNOW that variations on this have been asked and answered; I’ve been

  • 0

OK, I KNOW that variations on this have been asked and answered; I’ve been reading them all day, but I’m still stuck. So, here goes:

I need to create a summary list in HTML from some XML.

Given this XML:

<Root><!-- yes, I know I don't need a 'Root' element! Legacy code... -->
  <Plans>
    <Plan AreaID="1" UnitID="83">
      <Part ID="9122" Name="foo" />
      <Part ID="9126" Name="bar" />
    </Plan>
    <Plan AreaID="1" UnitID="86">
      <Part ID="8650" Name="baz" />
    </Plan>
    <Plan AreaID="2" UnitID="26">
      <Part ID="215" Name="quux" />
    </Plan>
    <Plan AreaID="1" UnitID="95">
      <Part ID="7350" Name="meh" />
    </Plan>
  </Plans>
</Root>

I need to emit:

<ol>
  <li>Area 1: 
    <ol><!-- units in Area 1 -->
      <li>Unit 83: 
        <ol>
          <li>Part 9122 (foo)</li>
          <li>Part 9126 (bar)</li>
        </ol>
      </li>
      <li>Unit 86: 
        <ol>
          <li>Part 8650 (baz)</li>
        </ol>
      <li>Unit 95: 
        <ol>
          <li>Part 7350 (meh)</li>
        </ol>
      </li>
    </ol><!-- /units in Area 1-->
  </li>
  <li>Area 2: 
    <ol><!-- units in Area 2 -->
      <li>Unit 26: 
        <ol>
          <li>Part 215 (quux)</li>
        </ol>
      </li>
    </ol><!-- /units in Area 2-->
  </li>
</ol>

I have the outer grouping working — I get top-level list elements for Area 1 and 2. But I can’t get the sequences of Units in the Areas — I either get no output, or repeating the same value. I haven’t even got down to the Part level 🙁

I’ve been working on a stylesheet like this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
<xsl:output method="html" indent="yes"/>

<xsl:key name="kAreaID" match="Plan" use="@AreaID" />
<xsl:key name="kUnitID" match="Plan" use="@UnitID" />

<xsl:template match="/Root/Plans">
<html><head><title>test grouping</title></head>
<body>
  <ol>
    <xsl:for-each select="./Plan[generate-id(.) = 
                      generate-id( key( 'kAreaID', @AreaID )[1] )]"
    >
      <xsl:sort order="ascending" select="./@AreaID" />
      <li>Area <xsl:value-of select="@AreaID"/>: 
        <ol>
          <xsl:for-each select="key( 'kUnitID', @UnitID )">
            <li>Unit <xsl:value-of select="@UnitID"/>: 
              <ol>
                <li>(Parts go here...)</li>
              </ol>
            </li>
          </xsl:for-each>
        </ol>
      </li>
    </xsl:for-each>
  </ol>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Any help is greatly appreciated!

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-11T22:02:28+00:00Added an answer on May 11, 2026 at 10:02 pm

    Here is the Muenchian grouping solution you are looking for.

    Going from the original XML you provided, I thought grouping by AreaID would be enough, but it turns out that a second grouping by UnitID is also needed.

    Here is my modified XSLT 1.0 solution. It’s not a lot more complex than the original solution:

    <xsl:stylesheet
      version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    >
    
      <xsl:key name="kPlanByArea" match="Plan" 
               use="@AreaID" />
      <xsl:key name="kPlanByAreaAndUnit" match="Plan" 
               use="concat(@AreaID, ',', @UnitID)" />
    
      <xsl:template match="/">
        <xsl:apply-templates select="Root/Plans" />
      </xsl:template>
    
      <!-- main template -->
      <xsl:template match="Plans">
        <ol>
          <!-- group by '{@AreaID}' (note the template mode!) -->
          <xsl:apply-templates mode="area-group" select="
            Plan[
              generate-id()
              =
              generate-id(
                key('kPlanByArea', @AreaID)[1]
              )
            ]
          ">
            <xsl:sort select="@AreaID" data-type="number" />
          </xsl:apply-templates>
        </ol>
      </xsl:template>
    
      <!-- template to output each '{@AreaID}' group -->
      <xsl:template match="Plan" mode="area-group">
        <li>
          <xsl:value-of select="concat('Area ', @AreaID)" />
          <ol>
            <!-- group by '{@AreaID},{@UnitID}' -->
            <xsl:apply-templates mode="unit-group" select="
              key('kPlanByArea', @AreaID)[
                generate-id()
                =
                generate-id(
                  key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))[1]
                )
              ]
            ">
              <xsl:sort select="@UnitID" data-type="number" />
            </xsl:apply-templates>
          </ol>
        </li>
      </xsl:template>
    
      <!-- template to output each '{@AreaID},{@UnitID}' group -->
      <xsl:template match="Plan" mode="unit-group">
        <li>
          <xsl:value-of select="concat('Unit ', @UnitID)" />
          <ol>
            <xsl:apply-templates select="
              key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))/Part
            ">
              <xsl:sort select="@UnitID" data-type="number" />
            </xsl:apply-templates>
          </ol>
        </li>
      </xsl:template>
    
      <!-- template to output Parts into a list -->
      <xsl:template match="Part">
        <li>
          <xsl:value-of select="concat('Part ', @ID, ' (', @Name ,')')" />
        </li>
      </xsl:template>
    
    </xsl:stylesheet>
    

    Since your XML is missing it, I added a UnitID to group on:

    <Plan AreaID="1" UnitID="86">
      <Part ID="8651" Name="zzz" />
    </Plan>
    

    And here is the output:

    <ol>
      <li>Area 1
        <ol>
          <li>Unit 83
            <ol>
              <li>Part 9122 (foo)</li>
              <li>Part 9126 (bar)</li>
            </ol>
          </li>
          <li>Unit 86
            <ol>
              <li>Part 8650 (baz)</li>
              <li>Part 8651 (zzz)</li>
            </ol>
          </li>
          <li>Unit 95
            <ol>
              <li>Part 7350 (meh)</li>
            </ol>
          </li>
        </ol>
      </li>
      <li>Area 2
        <ol>
          <li>Unit 26
            <ol>
              <li>Part 215 (quux)</li>
            </ol>
          </li>
        </ol>
      </li>
    </ol>
    

    Since you seem to have a hard time with the XSL key, here my attempt of an explanation:

    An <xsl:key> is absolutely equivalent to the associative array (map, hash, whatever you call it) known to many programming languages. This:

    <xsl:key name="kPlanByAreaAndUnit" match="Plan" 
             use="concat(@AreaID, ',', @UnitID)" />
    

    generates a data structure that could be expressed in JavaScript like this:

    var kPlanByAreaAndUnit = {
      "1,83": ['array of all <Plan> nodes with @AreaID="1" and @UnitID="83"'],
      "1,86": ['array of all <Plan> nodes with @AreaID="1" and @UnitID="86"'],
      /* ... */
      "1,95": ['array of all <Plan> nodes with @AreaID="1" and @UnitID="95"']
    };
    

    The function to access the data structure is called key(). So, this XPath expression:

    key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))
    

    is the logical equivalent of (in JavaScript, again):

    kPlanByAreaAndUnit[this.AreaID + ',' + this.UnitID];
    

    returning an array (a node-set, more correctly) of all nodes matching the given key string (the key is always a string). This node-set can be used like any other node-set in XSLT, i.e. like the ones you retrieve through “traditional” XPath. This means you can apply conditions (predicates) to it:

    <!-- first node only... -->
    key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))[1]
    
    <!-- nodes that have <Part> children only... -->
    key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))[Part]
    

    or use it as a base for XPath navigation:

    <!-- the actual <Part> children of matched nodes... -->
    key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))/Part
    

    and so on. This also means we can use it as a “select” expression for <xsl:apply-templates>, and we can use it as a base for grouping. Which leads us to the core of the above stylesheet (if you have wrapped your head around this one, you’ve understood the rest of the solution as well):

    key('kPlanByArea', @AreaID)[
      generate-id()
      =
      generate-id(
        key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))[1]
      )
    ]
    

    In JavaScript again, this could be expressed as:

    // the result will be a node-set, so we prepare an array
    var selectedNodes = [];
    
    // "key('kPlanByArea', @AreaID)"
    var nodeSet = kPlanByArea[this.AreaID];
    
    // "[...]" - the [] actually triggers a loop that applies 
    // the predicate expression to all nodes in the set, so we do:
    for (var i = 0; i < nodeSet.length; i++) {
       // use the current node for any calculations
       var c = nodeSet[i];
       if (
         // if the current node === the *first* node in kPlanByAreaAndUnit...
         generateId(c)
         ==
         generateId(kPlanByAreaAndUnit[c.AreaID + ',' + c.UnitID][0])
       ) {
         // ...include it in the resulting selection
         selectedNodes.push(c)
       }
    }
    

    After the expression is done, only those nodes are selected that are the respective first ones with a given “AreaID, UnitID” combination – effectively we have grouped them on their “AreaID, UnitID” combination.

    Applying a template to this node-set causes every combination to appear only once. My <xsl:template match="Plan" mode="unit-group"> then retrieves the full list again to achieve complete output for each group.

    I hope the use of JavaScript to explain the concept was a helpful idea.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I know that variations on this question have been asked, but I've tried all
I know that this sort of question has been asked here before, but still
I know variations of this have been asked before, but I was unable to
Variations of this question have been asked, but not specific to GNU/Linux and C.
The Disclaimer First of all, I know this question (or close variations) have been
I know that this question has been asked many times ( but in different
Variations on this question have been asked, but not this specifically. I have a
Variations of this question have been asked before, but it seems like the issue
I know, variations of this question had been asked before. But my case may
I know that hundreds of variations of these considerations have been posted all over

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.