Selecting unique combinations from two node sets

J

johkar

There are three unique loc/@id values and two unique coverageClass
values. I need to figure out how to output all the unique
combinations of the two (see example under original xml). Can anyone
please help?

<?xml version="1.0" encoding="UTF-8"?>
<company>
<company_bs>
<company_info>
<location>
<loc id="1"/>
</location>
<location>
<loc id="2"/>
</location>
<location>
<loc id="3"/>
</location>
<business>
<businessType>
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class2</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class2</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
</businessType>
</business>
</company_info>
</company_bs>
</company>

I would like to end up with all the unique location and class
combinations like so:

<company>
<combo><loc>1</loc><class>Class1</class></combo>
<combo><loc>1</loc><class>Class2</class></combo>
<combo><loc>2</loc><class>Class1</class></combo>
<combo><loc>2</loc><class>Class2</class></combo
<combo><loc>3</loc><class>Class1</class></combo>
<combo><loc>3</loc><class>Class2</class></combo>
</company>
 
M

Martin Honnen

johkar said:
I would like to end up with all the unique location and class
combinations like so:

<company>
<combo><loc>1</loc><class>Class1</class></combo>
<combo><loc>1</loc><class>Class2</class></combo>
<combo><loc>2</loc><class>Class1</class></combo>
<combo><loc>2</loc><class>Class2</class></combo
<combo><loc>3</loc><class>Class1</class></combo>
<combo><loc>3</loc><class>Class2</class></combo>
</company>

With XSLT 2.0 (as supported by Saxon from http://saxon.sourceforge.net/
and AltovaXML tools from http://www.altova.com/ and Gestalt from
http://gestalt.sourceforge.net/) you can use

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">

<xsl:variable name="root" select="/"/>

<xsl:template match="company">
<xsl:copy>
<xsl:for-each
select="distinct-values(company_bs/company_info/location/loc/@id)">
<xsl:variable name="id" select="."/>
<xsl:for-each
select="distinct-values($root/company/company_bs/company_info/business/businessType/businessClass/class/coverageClass)">
<combo><loc><xsl:value-of
select="$id"/></loc><class><xsl:value-of select="."/></class></combo>
<xsl:value-of select="'
'"/>
</xsl:for-each>
</xsl:for-each>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>

With XSLT 1.0 you can apply Muenchian grouping:

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:key name="by-id" match="loc" use="@id"/>

<xsl:key name="by-class" match="coverageClass" use="."/>

<xsl:template match="company">
<xsl:copy>
<xsl:for-each
select="company_bs/company_info/location/loc[generate-id() =
generate-id(key('by-id', @id)[1])]">
<xsl:variable name="id" select="@id"/>
<xsl:for-each
select="/company/company_bs/company_info/business/businessType/businessClass/class/coverageClass[generate-id()
= generate-id(key('by-class', .)[1])]">
<combo><loc><xsl:value-of
select="$id"/></loc><class><xsl:value-of select="."/></class></combo>
<xsl:value-of select="'
'"/>
</xsl:for-each>
</xsl:for-each>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>
 
D

David Carlisle

in xslt2 something like the following. If you are stuck with xslt 1,
then the usual technique is "muenchian grouping" (google for that).

David


<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:eek:utput indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/">
<xsl:variable name="l"
select="distinct-values(/company/company_bs/company_info/location/loc/@id)"/>
<xsl:variable name="c"
select="distinct-values(/company/company_bs/company_info/business/businessType/businessClass/class/coverageClass)"/>

<company>
<xsl:for-each select="$l">
<xsl:variable name="loc" select="."/>
<xsl:for-each select="$c">
<combo><loc><xsl:value-of select="$loc"/></loc><class><xsl:value-of
select="."/></class></combo>
</xsl:for-each>
</xsl:for-each>
</company>
</xsl:template>

</xsl:stylesheet>
 
J

johkar

in xslt2 something like the following. If you are stuck with xslt 1,
then the usual technique is "muenchian grouping" (google for that).

David

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:eek:utput indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/">
<xsl:variable name="l"
select="distinct-values(/company/company_bs/company_info/location/loc/@id)"­/>
<xsl:variable name="c"
select="distinct-values(/company/company_bs/company_info/business/businessT­ype/businessClass/class/coverageClass)"/>

<company>
<xsl:for-each select="$l">
<xsl:variable name="loc" select="."/>
<xsl:for-each select="$c">
<combo><loc><xsl:value-of select="$loc"/></loc><class><xsl:value-of
select="."/></class></combo>
</xsl:for-each>
</xsl:for-each>
</company>
</xsl:template>

</xsl:stylesheet>

Thanks for the reply.
 
J

johkar

johkar said:
I would like to end up with all the unique location and class
combinations like so:
<company>
<combo><loc>1</loc><class>Class1</class></combo>
<combo><loc>1</loc><class>Class2</class></combo>
<combo><loc>2</loc><class>Class1</class></combo>
<combo><loc>2</loc><class>Class2</class></combo
<combo><loc>3</loc><class>Class1</class></combo>
<combo><loc>3</loc><class>Class2</class></combo>
</company>

With XSLT 2.0 (as supported by Saxon fromhttp://saxon.sourceforge.net/
and AltovaXML tools fromhttp://www.altova.com/and Gestalt fromhttp://gestalt.sourceforge.net/) you can use

<xsl:stylesheet
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="2.0">

   <xsl:variable name="root" select="/"/>

   <xsl:template match="company">
     <xsl:copy>
       <xsl:for-each
select="distinct-values(company_bs/company_info/location/loc/@id)">
         <xsl:variable name="id" select="."/>
         <xsl:for-each
select="distinct-values($root/company/company_bs/company_info/business/businessType/businessClass/class/coverageClass)">
           <combo><loc><xsl:value-of
select="$id"/></loc><class><xsl:value-of select="."/></class></combo>
           <xsl:value-of select="'
'"/>
         </xsl:for-each>
       </xsl:for-each>
     </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

With XSLT 1.0 you can apply Muenchian grouping:

<xsl:stylesheet
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="1.0">

   <xsl:key name="by-id" match="loc" use="@id"/>

   <xsl:key name="by-class" match="coverageClass" use="."/>

   <xsl:template match="company">
     <xsl:copy>
       <xsl:for-each
select="company_bs/company_info/location/loc[generate-id() =
generate-id(key('by-id', @id)[1])]">
         <xsl:variable name="id" select="@id"/>
         <xsl:for-each
select="/company/company_bs/company_info/business/businessType/businessClass/class/coverageClass[generate-id()
= generate-id(key('by-class', .)[1])]">
           <combo><loc><xsl:value-of
select="$id"/></loc><class><xsl:value-of select="."/></class></combo>
           <xsl:value-of select="'
'"/>
         </xsl:for-each>
       </xsl:for-each>
     </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

Actually I have a slightly different scenario than I envisioned. loc/
@id has its own businessType and I need to know the coverageClass's
that are available to that location....they are no longer in the same
nodeset.

<?xml version="1.0" encoding="UTF-8"?>
<company>
<company_bs>
<company_info>
<location>
<loc id="1"/>
</location>
<location>
<loc id="2"/>
</location>
<location>
<loc id="3"/>
</location>
<business>
<businessType loc="1">
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class2</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class2</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
</businessType>
<businessType loc="2">
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class3</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class3</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class4</coverageClass>
</class>
</businessClass>
</businessType>
<businessType loc="3">
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class2</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class2</coverageClass>
</class>
</businessClass>
<businessClass>
<class>
<coverageClass>Class1</coverageClass>
</class>
</businessClass>
</businessType>
</business>
</company_info>
</company_bs>
</company>

I would like to end up with all the unique location and class
combinations like so:

<company>
<combo><loc>1</loc><class>Class1</class></combo>
<combo><loc>1</loc><class>Class2</class></combo>
<combo><loc>2</loc><class>Class1</class></combo>
<combo><loc>2</loc><class>Class3</class></combo>
<combo><loc>2</loc><class>Class4</class></combo
<combo><loc>3</loc><class>Class1</class></combo>
<combo><loc>3</loc><class>Class2</class></combo>
</company>
 
M

Martin Honnen

johkar said:
Actually I have a slightly different scenario than I envisioned. loc/
@id has its own businessType and I need to know the coverageClass's
that are available to that location....they are no longer in the same
nodeset.


Here is an XSLT 2.0 solution:

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">

<xsl:strip-space elements="*"/>
<xsl:eek:utput method="xml" indent="yes"/>

<xsl:key name="bt-by-loc" match="businessType" use="@loc"/>
<xsl:variable name="root" select="/"/>

<xsl:template match="company">
<xsl:for-each
select="distinct-values(company_bs/company_info/location/loc/@id)">
<xsl:variable name="id" select="."/>
<xsl:for-each select="distinct-values(key('bt-by-loc', $id,
$root)/businessClass/class/coverageClass)">
<combo>
<loc>
<xsl:value-of select="$id"/>
</loc>
<class>
<xsl:value-of select="."/>
</class>
</combo>
</xsl:for-each>
</xsl:for-each>
</xsl:template>

</xsl:stylesheet>
 
J

johkar

johkarwrote:

Here is an XSLT 2.0 solution:

<xsl:stylesheet
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="2.0">

   <xsl:strip-space elements="*"/>
   <xsl:eek:utput method="xml" indent="yes"/>

   <xsl:key name="bt-by-loc" match="businessType" use="@loc"/>
   <xsl:variable name="root" select="/"/>

   <xsl:template match="company">
     <xsl:for-each
select="distinct-values(company_bs/company_info/location/loc/@id)">
       <xsl:variable name="id" select="."/>
       <xsl:for-each select="distinct-values(key('bt-by-loc', $id,
$root)/businessClass/class/coverageClass)">
         <combo>
           <loc>
             <xsl:value-of select="$id"/>
           </loc>
           <class>
             <xsl:value-of select="."/>
           </class>
         </combo>
       </xsl:for-each>
     </xsl:for-each>
   </xsl:template>

</xsl:stylesheet>

Thanks Martin, I was called away and couldn't aknowledge your answer.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top