Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Garrett Fisher 341 posts 496 karma points
    Jul 08, 2010 @ 22:27
    Garrett Fisher
    0

    getting a unique list of values from a data field

    Hi,

    I am having a HECK of a time trying to get a list of unique values from a certain field in a group of pages.  I have tried several different ways, but in effort to keep the code-pasting and -reading to a minimum, I'll just describe the two main techniques I'm trying.

    I have a large group of pages of document type alias "SolutionItem".  These pages have a data field I created on them called "Responsibility".  Many of them share the same value, therefore when I try to create a dropdown to display them, I'd like only to show one of each.  Like a SELECT DISTINCT Responsibility FROM ... in SQL. With me so far? Ok.  So I tried this:

    <xsl:for-each select="$currentPage/descendant-or-self::node/node [@nodeTypeAlias = 'SolutionItem' and contains(data[@alias = 'Industry'], $industry)]">
    <xsl:sort select="data [@alias = 'Responsibility']" order="ascending" />                  
         <xsl:if test="data [@alias = 'Responsibility'][not(.=following::data [@alias = 'Responsibility'])]">
    .....


    ...and this gives me a very incomplete list.  In other words, there are several SolutionItems which contain the given $industry whose Responsibility field value is never returned/written.  I cannot figure out why.  preceding::data [] yields the same result.

    I am also trying:

    <xsl:for-each select="$currentPage/descendant-or-self::node/node [@nodeTypeAlias = 'SolutionItem' and contains(data[@alias = 'Industry'], $industry)]">
    <xsl:sort select="data [@alias = 'Responsibility']" order="ascending" />
    <xsl:if test="data [@alias = 'Responsibility'] != following::data [@alias = 'Responsibility']">
    .....


    And in this scenario I am getting many duplicates, even though when I write out the values of the current loop item value (<xsl:value-of select="data [@alias = 'Responsibility']"/>) and the folllowing item (<xsl:value-of select="following::data [@alias = 'Responsibility']"/>), I am indeed getting different values.

    What am I doing wrong? I've spent HOURS on this problem and it just doesn't seem like something that should be that difficult.

    Any help would be VERY sincerely appreciated!

    Thanks in advance,

    Garrett

  • Lee Kelleher 4026 posts 15836 karma points MVP 13x admin c-trib
    Jul 08, 2010 @ 23:25
    Lee Kelleher
    1

    Hi Garrett,

    I completely understand how frustrating it is trying to do grouping in XSLT ... hopefully I can help?

    The approach we are going to take is the "Muenchian Method".  Above the first <xsl:template> tag place the following:

    <xsl:key name="responsibility" match="data[@alias='Responsibility']" use="text()" />

    This will provide an index for the 'Responsibility' values - which we'll get on to next.

    The following snippet will have a very ugly XPath statement, I'll try to explain it later...

    <select>
        <xsl:for-each select="$currentPage/descendant-or-self::node[@nodeTypeAlias = 'SolutionItem' and contains(data[@alias = 'Industry'], $industry)]/data[@alias='Responsibility' and count(. | key('responsibility', text())[1]) = 1]">
            <option>
                <xsl:value-of select="text()" />
            </option>
        </xsl:for-each>
    </select>

    ... this should give you a dropdown list of the distinct values from the 'Responsibility' data items.

    For a breakdown of the XPath statement... the first part:

    $currentPage/descendant-or-self::node[@nodeTypeAlias = 'SolutionItem' and contains(data[@alias = 'Industry'], $industry)]

    This gets all of the 'SolutionItem' nodes that contain the specified 'Industry' ... if you wanted to put that in a separate <xsl:variable> you could do, just to make your XPath a little shorter.

    Next...

    /data[@alias='Responsibility'

    This specifies that you want to loop through the data items called 'Responsibility' ... and the second part...

     and count(. | key('responsibility', text())[1]) = 1]

    ... makes use of the index (that we included at the start).  The key performs a check to return a distinct node-set.

    There is a more academic explanation to the "Muenchian Method", but it makes go cross-eyed - I just know that it works.

    I can't guarantee that this is going to work first time for you, as I've never even seen your data - but hopefully it's a step in the right direction.

    Good luck.

    Cheers, Lee.

  • Sascha Wolter 615 posts 1101 karma points
    Jul 08, 2010 @ 23:36
    Sascha Wolter
    0

    Hi Garrett,

    don't know if this helps, but here goes. I had a similar problem a while ago. The 'Resource' node contains a number of 'Resource item' nodes which contain a property 'year'. Now the output on the Resource node should be a nicely formatted list of all Resource items underneath it, sorted by date descending and only displaying the date on the first item where it appears. 

    So I grab all the child nodes:

    <xsl:for-each select="node[@nodeTypeAlias='Resource item']">
       <xsl:sort select="data[@alias='year']" order="descending"/>

    and then declare a new variable which holds the information if the current node in the for-each has a new year (aka different from the previous sibling):

     

       <xsl:variable name="isNewYear" select="position() != 1 and data[@alias='year'] != preceding-sibling::node/data[@alias='year']" />

    Obviously position 1 will always need outputting.

    Hope this is what you're looking for or at least gets you going in the right direction. :)

    Sascha

  • Garrett Fisher 341 posts 496 karma points
    Jul 09, 2010 @ 15:48
    Garrett Fisher
    0

    Hi,

    A sincere thanks to both of yuo for your speedy and diligent replies.  However, I am sorry to say that neither solution is working.

    Lee,  using your <xsl:key> code, I am getting a similar result as when using [data [@alias = 'Responsibility'][not(.=following::data [@alias = 'Responsibility'])]].  Meaning I am getting More results But there are entries missing (???).  I can tell this because when I take out the uniqueness-filter attempts, I get instances of the Responsibility column that do not show up when I put the filter In.  They just appear in multiple. 

    Sascha, using your code I am simply getting every instance of the data [@alias = 'Responsibility'] column, I believe because I don't think the value of xsl:variable can be changed once it is declared, so it returns true the first time and then and stays true so it writes out every row.

    Lee-- I am doing a direct query on the database using what I BELIEVE to be the SQL equivalent to what we're trying to do here:

    SELECT responsibility FROM `ignite_solutions`.`solutions` WHERE industry LIKE '%Retail%'
    GROUP BY responsibility
    ORDER BY responsibility

    And I am getting all matching Responsibility entries.  However, using the Muenchian method, I am missing ONE.  Any ideas on what I might be able to furnish you with to maybe take a deeper look at it?  Here now is my complete code as I have it now:

    <xsl:key name="distinct-responsibility" match="data[@alias='Responsibility']" use="text()" />
    <xsl:template match="/">

    <select id="responsibility" name="responsibility" type="text" class="responsibilityText" onchange="resetChallenge();document.forms['find-solution'].submit();return false;">
    <option value="">My Responsibility &gt;</option>
    <xsl:for-each select="$currentPage/descendant-or-self::node [@nodeTypeAlias = 'SolutionItem' and contains(data[@alias = 'Industry'], $industry)]/data[@alias='Responsibility' and count(. | key('distinct-responsibility', text())[1]) = 1]">
    <xsl:sort select="data [@alias = 'Responsibility']" order="ascending" />
    <option value="{data [@alias = 'Responsibility']}">
    <xsl:value-of select="text()" />
    </option>
    </xsl:for-each>
    </select>

    Any more help you could offer would be sincerely appreciated.  I've now been stuck on this for a full work day!

    Earnestly,

    Garrett

  • Sascha Wolter 615 posts 1101 karma points
    Jul 09, 2010 @ 17:19
    Sascha Wolter
    0

    Hi Garrett,

    sorry that none of this seems to help. As to the xsl:variable being changed: the only exception is inside of a for-each loop where it gets updated with each iteration (see http://xml.apache.org/xalan-j/xsltc/xsl_variable_design.html). Oddly it seems to be working for me, unfortunately can't show you the site as it's for a client.

    However you don't have to declare a variable, this should work as well:

    <xsl:for-each select="$currentPage/descendant-or-self::node/node [@nodeTypeAlias = 'SolutionItem' and contains(data[@alias = 'Industry'], $industry)]">

    <xsl:sort select="data [@alias = 'Responsibility']" order="ascending" />                  
         <xsl:if test="position() = 1 or data[@alias='Responsibility'] != preceding-sibling::node/data[@alias='Responsibility']">
    .....
    The if statement checks if this is the first item or if the preceding sibling has a different value in data[@alias='Responsibility']. As they haven been 'grouped' before with the sort statement that should give a distinct list of Responsibility values. If that doesn't work than I'm afraid I'm out of my depth. 
    Obviously Lee's solution is the more elegant one anyway... :D
    Hope you can crack that soon,
    Sascha

  • Garrett Fisher 341 posts 496 karma points
    Jul 09, 2010 @ 17:38
    Garrett Fisher
    0

    Hello again, Sascha.   And thanks.  But no, this just isn't working.  I'm getting:

    Corporate Communications
    Corporate Communications
    Corporate Communications
    Creative Development
    Creative Development
    General Management
    General Management
    General Management
    Human Resources
    Human Resources
    Human Resources

    Etc.  It's a weird one for sure.  For now I'm going with the one that just misses one and I'll keep looking closely at the data.

    //Garrett

  • Lee Kelleher 4026 posts 15836 karma points MVP 13x admin c-trib
    Jul 12, 2010 @ 08:40
    Lee Kelleher
    0

    Hi Garrett, (I'd been away over the weekend - hence no reply from me)

    Which data-type are you using for the "Responsibility" field? Is it a standard textbox, or have you set-up a custom drop-down list?

    If you are using a custom drop-down list, then you can use PreValues to pull out all of values.

    Using a standard textbox field, then its either battle with the XSLT distinct/grouping ... or like you've done, write a direct SQL query?

    If you do want to go ahead with the XSLT distinct/grouping, then it might be worth posting a snippet of your XML content (from your /data/umbraco.config), so we can see the structure of the data we are dealing with - in terms of the nodeTypeAlias and Industry field, etc. (Gives me something to test against).

    Cheers, Lee.

  • Garrett Fisher 341 posts 496 karma points
    Jul 12, 2010 @ 15:45
    Garrett Fisher
    0

    Hi Lee.  Thanks for your continued attention.  Hope you had a great weekend away.

    How do I print out the entire XML document I'm transforming?

    //Garrett

  • Steen Tøttrup 191 posts 291 karma points c-trib
    Jul 12, 2010 @ 15:57
    Steen Tøttrup
    0
    <xsl:template match="*">
    <xsl:copy-of select="." />
    </xsl:template>

    This is from memory, so I hope it works!

    /Steen

     

Please Sign in or register to post replies

Write your reply to:

Draft