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)]">
...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)]">
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.
... 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.
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.
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. :)
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:
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-eachselect="$currentPage/descendant-or-self::node/node [@nodeTypeAlias = 'SolutionItem' and contains(data[@alias = 'Industry'], $industry)]">
<xsl:iftest="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
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.
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).
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:
...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:
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
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:
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...
... 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:
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...
This specifies that you want to loop through the data items called 'Responsibility' ... and the second part...
... 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.
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: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
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:
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:
Any more help you could offer would be sincerely appreciated. I've now been stuck on this for a full work day!
Earnestly,
Garrett
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:
.....
Hello again, Sascha. And thanks. But no, this just isn't working. I'm getting:
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
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.
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
This is from memory, so I hope it works!
/Steen
is working on a reply...