Home > Net >  Find the most popular attribute across all nodes with a same xpath in XSLT 1.0
Find the most popular attribute across all nodes with a same xpath in XSLT 1.0

Time:02-01

Given the example below, I would like the output to be commonest param is: ratio, which is the most popular name for a parameter.

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <commands>
        <commandsgroup name="FLDSMDFR">
            <command commands_type="WEATHER" name="start"/>
            <command commands_type="WEATHER" name="stop"/>
            <command commands_type="WEATHER" name="reset"/>
            <command commands_type="WEATHER" name="set_portion_size">
                <parameter  under_control="false" name="portion_size"/>
            </command>
            <command commands_type="WEATHER" name="set_meatball_ratio">
                <parameter under_control="false" name="ratio"/>
            </command>
            <command commands_type="WEATHER" name="set_desserts_ratio">
                <parameter under_control="false" name="ratio"/>
            </command>
            <command commands_type="WEATHER" name="set_precip_frequency">
                <parameter under_control="false" name="frequency"/>
            </command>
            <command commands_type="WEATHER" name="set_weather_intensity">
                <parameter under_control="false" name="weather_intensity"/>
            </command>
            <command commands_type="UI" name="use_fishing_metaphors">
                <parameter under_control="false" name="are_fishing_metaphors"/>
            </command>
            <command commands_type="UI" name="allow_spray_on_shoes">
                <parameter under_control="true" name="are_spray_on_shoes"/>
            </command>
        </commandsgroup>
    </commands>
</root>  

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

    <xsl:template match="/root">
        <!-- stuff ... and then: -->
        <xsl:call-template name="get_commonest_param"/>
    </xsl:template>

    <xsl:key name="parameters-by-name" match="parameter" use="@name"/>
    <!-- objective here is to find the most frequently use parameter name in all commands -->
    <xsl:template name="get_commonest_param">
        <xsl:for-each select="/root/commands/commandsgroup/command[@commands_type='WEATHER']/parameter[@under_control='false'][count(. | key('parameters-by-name',@name)[1])=1]">
            <xsl:sort select="count(parameter)" data-type="number" order="ascending"/>
            <xsl:if test="position()=last()">
                <xsl:message>
                    <xsl:value-of select="concat('commonest param is: ',./@name)"/>
                </xsl:message>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

My output seems incorrect to me. Using xsltproc (which is 1.0) in redhats I get:

[brent@babybrentsardines Desktop]$ xsltproc cloudy.xslt cloudy.xml
commonest param is: weather_intensity

FWIW: I am committed to xsl 1.0 but I'm not committed to using the Muenchane method. Ideas?

CodePudding user response:

Well, you should be committed to the Muenchian method. This consists of two parts: first, you isolate the distinct values; then you select the nodes that share the value common to each group. You omitted the 2nd part.

Consider this simplified example:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>

<xsl:key name="param-by-name" match="parameter" use="@name"/>

<xsl:template match="/root">
    <!-- for each distinct name -->
    <xsl:for-each select="commands/commandsgroup/command/parameter[count(. | key('param-by-name', @name)[1])=1]">
        <!-- sort by size of group -->
        <xsl:sort select="count(key('param-by-name', @name))" data-type="number" order="descending"/>
        <!-- output the most frequent value -->
        <xsl:if test="position()=1">
            <xsl:message>
                <xsl:text>commonest param is: </xsl:text>
                <xsl:value-of select="@name"/>
            </xsl:message>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Note that when you add predicates to the expression that selects the distinct values, but not to the key's match pattern, you may get incorrect results if there can be nodes that match the pattern but do not satisfy the conditions in the predicates.

  • Related