Another Example Sorting Data with xsl:sort

page 112 xsl:text xsl:text xsl:value-of select=zip xsl:value-of select=newline xsl:value-of select=newline xsl:for-each xsl:template xsl:stylesheet Here are the results, only those addresses from states beginning with the letter M , sorted by first name within last name: Ms. Natalie Attired 707 Breitling Way Winter Harbor, ME 00218 Harry Backstayge 283 First Avenue Skunk Haven, MA 02718 Mary Backstayge 283 First Avenue Skunk Haven, MA 02718 Ms. Amanda Reckonwith 930-A Chestnut Street Lynn, MA 02930 Notice that in the xsl:for-each element, we used a predicate in our XPath expression so that only addresses containing state elements whose contents begin with M are selected. This example starts us on the path to grouping nodes. We could do lots of other things here: • We could generate output that prints all the unique Zip Codes, along with the number of addresses that have those Zip Codes. • For each unique Zip Code or state, or last name, etc. we could sort on a field and list all addresses with that Zip Code. Well discuss these topics in the next section.

6.2 Grouping Nodes

When grouping nodes, we sort things to get them into a certain order, then we group all items that have the same value for the sort key or keys. Well use xsl:sort for this grouping, then use variables or functions like key or generate-id to finish the job.

6.2.1 Our First Attempt

For our first example, well take our list of addresses and group them. Well look for all unique values of the zip element and list the addresses that match each one. What well do is sort the list by Zip Code, then go through the list. If a given item doesnt match the previous Zip Code, well print out a heading; if it does match, well just print out the address. Heres our first attempt: ?xml version=1.0? xsl:stylesheet version=1.0 xmlns:xsl=http:www.w3.org1999XSLTransform xsl:output method=text indent=no xsl:variable name=newline xsl:text xsl:text xsl:variable xsl:template match= xsl:textAddresses sorted by Zip Codexsl:text xsl:value-of select=newline page 113 xsl:for-each select=addressbookaddress xsl:sort select=zip xsl:if test=zip=preceding-sibling::address[1]zip xsl:value-of select=newline xsl:textZip code xsl:text xsl:value-of select=zip xsl:text xsl:text xsl:value-of select=city xsl:text, xsl:text xsl:value-of select=state xsl:text: xsl:text xsl:value-of select=newline xsl:if xsl:if test=nametitle xsl:value-of select=nametitle xsl:text xsl:text xsl:if xsl:value-of select=namefirst-name xsl:text xsl:text xsl:value-of select=namelast-name xsl:value-of select=newline xsl:value-of select=street xsl:value-of select=newline xsl:value-of select=newline xsl:for-each xsl:template xsl:stylesheet Our approach in this stylesheet consists of two steps: 1. Sort the addresses by Zip Code. xsl:sort select=zip 2. For each Zip Code, if it doesnt match the previous Zip Code, print out a heading, then print out the addresses that match it. 3. xsl:if test=zip=preceding-sibling::address[1]zip 4. xsl:value-of select=newline 5. xsl:textZip code xsl:text ... Remember that preceding-sibling returns a NodeSet , so preceding- sibling::address[1] represents the first preceding sibling. That sounds reasonable, doesnt it? Lets take a look at the results: Addresses sorted by Zip Code Zip code 00218 Winter Harbor, ME: Ms. Natalie Attired 707 Breitling Way Zip code 02718 Skunk Haven, MA: Mary Backstayge 283 First Avenue Harry Backstayge 283 First Avenue Zip code 02930 Lynn, MA: Ms. Amanda Reckonwith 930-A Chestnut Street