randombits
randombits

Reputation: 48490

XSLT determine which attribute is repeated the most

I have some difficult markup I'm working with that doesn't have normalized IDs in it. Let me show by example of what said markup might look like:

<listing>
    <foo fooid="81">Foo</foo>
    <bar barid="88">Bar</bar>
</listing>
<listing>
    <foo fooid="82">Foo</foo>
    <bar barid="81">Bar</bar>
</listing>
<listing>
    <foo fooid="83">Foo</foo>
    <bar barid="81">Bar</bar>
</listing>

The ID that I need to be able to stuff into an <xsl:variable /> in this case is 81 because the id is represented the most amount of times, regardless of its tags. Is there a trivial way to do this with XSLT? Ideally when it's said and done, I'll have something like:

<xsl:variable name="an_id"> where an_id is equal to 81

The known points are the following:

I am using XSLT 1.0. The variable name is not dynamic. It is static, can be called whatever as long as the value it derives to is the 81 above.

** UPDATE **

The XML I pasted above was sample and although I think Mads answer is correct, it doesn't parse so I'm updating with the real XML I'm dealing with. You can see the equivalent in my contrived example that <foo> and <bar> are trying to tackle the <hometeam> and <awayteam> tags, those are the key ones to focus in on.

<?xml version="1.0" standalone="no"?>
<message>
  <XML_File_ID>18996672</XML_File_ID>
  <heading>ABX%PHI-SKED</heading>
  <category>Statistics</category>
  <sport>NFL</sport>
  <title>2013 Philadelphia Eagles Schedule/Results</title>
  <season>2013</season>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4189</schedule_id>
    <Game_Date>09/09/2013</Game_Date>
    <Game_Time>06:55 PM</Game_Time>
    <week>1</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="081">Philadelphia</awayteam>
    <hometeam hometeamid="088">Washington</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>FedEx Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>ESPN</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4206</schedule_id>
    <Game_Date>09/15/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>2</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="083">San Diego</awayteam>
    <hometeam hometeamid="081">Philadelphia</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Lincoln Financial Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>CBS</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4253</schedule_id>
    <Game_Date>09/19/2013</Game_Date>
    <Game_Time>08:25 PM</Game_Time>
    <week>3</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="072">Kansas City</awayteam>
    <hometeam hometeamid="081">Philadelphia</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Lincoln Financial Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>NFL Network</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4331</schedule_id>
    <Game_Date>09/29/2013</Game_Date>
    <Game_Time>04:25 PM</Game_Time>
    <week>4</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="081">Philadelphia</awayteam>
    <hometeam hometeamid="067">Denver</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Sports Authority Field at Mile High Stadium</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4366</schedule_id>
    <Game_Date>10/06/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>5</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="081">Philadelphia</awayteam>
    <hometeam hometeamid="079">NY Giants</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>MetLife Stadium</Location>
    <Indoor>False</Indoor>
    <Turf>True</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4403</schedule_id>
    <Game_Date>10/13/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>6</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="081">Philadelphia</awayteam>
    <hometeam hometeamid="087">Tampa Bay</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Raymond James Stadium</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4419</schedule_id>
    <Game_Date>10/20/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>7</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="066">Dallas</awayteam>
    <hometeam hometeamid="081">Philadelphia</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Lincoln Financial Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4197</schedule_id>
    <Game_Date>10/27/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>8</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="079">NY Giants</awayteam>
    <hometeam hometeamid="081">Philadelphia</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Lincoln Financial Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4216</schedule_id>
    <Game_Date>11/03/2013</Game_Date>
    <Game_Time>04:05 PM</Game_Time>
    <week>9</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="081">Philadelphia</awayteam>
    <hometeam hometeamid="073">Oakland</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Oakland-Alameda County Coliseum</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4230</schedule_id>
    <Game_Date>11/10/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>10</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="081">Philadelphia</awayteam>
    <hometeam hometeamid="069">Green Bay</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Lambeau Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4251</schedule_id>
    <Game_Date>11/17/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>11</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="088">Washington</awayteam>
    <hometeam hometeamid="081">Philadelphia</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Lincoln Financial Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4302</schedule_id>
    <Game_Date>12/01/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>13</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="086">Arizona</awayteam>
    <hometeam hometeamid="081">Philadelphia</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Lincoln Financial Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4322</schedule_id>
    <Game_Date>12/08/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>14</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="068">Detroit</awayteam>
    <hometeam hometeamid="081">Philadelphia</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Lincoln Financial Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4350</schedule_id>
    <Game_Date>12/15/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>15</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="081">Philadelphia</awayteam>
    <hometeam hometeamid="076">Minnesota</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Mall of America Field at HHH Metrodome</Location>
    <Indoor>True</Indoor>
    <Turf>True</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4365</schedule_id>
    <Game_Date>12/22/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>16</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="063">Chicago</awayteam>
    <hometeam hometeamid="081">Philadelphia</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>Lincoln Financial Field</Location>
    <Indoor>False</Indoor>
    <Turf>False</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <listing>
    <season_type>Regular</season_type>
    <schedule_id>4391</schedule_id>
    <Game_Date>12/29/2013</Game_Date>
    <Game_Time>01:00 PM</Game_Time>
    <week>17</week>
    <ascore>0</ascore>
    <hscore>0</hscore>
    <awayteam awayteamid="081">Philadelphia</awayteam>
    <hometeam hometeamid="066">Dallas</hometeam>
    <TypeUpdate>P</TypeUpdate>
    <Status>
    </Status>
    <Location>AT&amp;T Stadium</Location>
    <Indoor>False</Indoor>
    <Turf>True</Turf>
    <TV_Listing>FOX</TV_Listing>
  </listing>
  <time_stamp> September 3, 2013, at 05:17 PM ET </time_stamp>
</message>

Basically I'm trying to get the home team's ID. Since it's not normalized above before the listings, I have to go for the team that's listed in every listing (and thus shows up the most). In this case it would be Philadelphia who has an ID of "081", but once you run number() on that in the XSLT processor, it should just be 81.

Here's the stylesheet I have working so far based on Mads answer:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" omit-xml-declaration="yes" />
  <xsl:strip-space elements="*" />

  <xsl:key name="id" match="@awayteamid | @hometeamid" use="."/>

  <xsl:template match="/message">
    <xsl:variable name="heading"><xsl:value-of select="heading"/></xsl:variable>
    <xsl:variable name="title"><xsl:value-of select="title"/></xsl:variable>
    <xsl:variable name="team_id">
      <xsl:for-each select="*/listing/*/@*">
         <xsl:sort select="count(key('id', .))" order="descending" data-type="number" />
         <xsl:if test="position()=1">
             <xsl:value-of select="."/>
         </xsl:if>
     </xsl:for-each>
   </xsl:variable>

    {
     "team_id": <xsl:value-of select="number($team_id)" /> 
    }
  </xsl:template>
</xsl:stylesheet>

And finally the error I'm getting using it (assuming it's because $team_id is not properly being set)

#<JSON::ParserError: 203: unexpected token at 'NaN 
    }
  '>

Upvotes: 2

Views: 110

Answers (2)

Mads Hansen
Mads Hansen

Reputation: 66796

You can define a key that matches on either of those attributes:

<xsl:key name="id" match="@fooid | @barid" use="."/>

And then create the an_id variable who's value is the first item from a sorted list of the attribute values, using the count of items in the key matching the attribute value.

<xsl:variable name="an_id">
   <xsl:for-each select="*/listing/*/@*">
       <xsl:sort select="count(key('id', .))" order="descending" data-type="number"/>
       <xsl:if test="position()=1">
           <xsl:value-of select="."/>
       </xsl:if>
   </xsl:for-each>
</xsl:variable>

Updated answer using your XML and XSLT (which you almost had, just needed to adjust the XPath in your for-each to account for what the context node is and where you are "standing" when the expression gets evaluated):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" omit-xml-declaration="yes" />
    <xsl:strip-space elements="*" />

    <xsl:key name="id" match="@awayteamid | @hometeamid" use="."/>

    <xsl:template match="/message">
        <xsl:variable name="heading"><xsl:value-of select="heading"/></xsl:variable>
        <xsl:variable name="title"><xsl:value-of select="title"/></xsl:variable>
        <xsl:variable name="team_id">
            <xsl:for-each select="listing/*/@*">
                <xsl:sort select="count(key('id', .))" order="descending" data-type="number" />
                <xsl:if test="position()=1">
                    <xsl:value-of select="."/>
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>

        {
        "team_id": <xsl:value-of select="number($team_id)" /> 
        }
    </xsl:template>
</xsl:stylesheet>

Upvotes: 2

Sean B. Durkin
Sean B. Durkin

Reputation: 12729

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" />

<xsl:key name="kFoobars" match="foo/@fooid | bar/@fooid |
                                foo/@barid | bar/@barid" use="." />

<xsl:template match="/*">
  <xsl:variable name="foobar-els" select="*/foo | */bar" />
  <xsl:variable name="foobar-atr" select="$foobar-els/@fooid | $foobar-els/@barid" />
  <xsl:for-each select="$foobar-atr[ generate-id() =
                                     generate-id( key('kFoobars',.)[1])]">
    <xsl:sort select="count(key('kFoobars',.))" data-type="number" order="descending" />
    <xsl:if test="position()=1">
      <t most-populous-id-value="{.}" />
    </xsl:if>  
  </xsl:for-each>  
</xsl:template>

</xsl:stylesheet>

Upvotes: 0

Related Questions