2010-03-20 19 views
5

En este archivo catalog.xml. Tengo dos libros que tienen el mismo inventario (es decir, 20). Quiero escribir un archivo XSL que muestre la mayor cantidad de copias de un libro en un catálogo. Si hay dos o más libros del mismo inventario, entonces deben mostrarse.XSL para mostrar el mayor número de copias en el archivo catalog.xml

<catalog> 
    <Book> 
    <sku>12345</sku> 
    <title>Beauty Secrets</title> 
    <condition>New</condition> 
    <current_inventory>20</current_inventory> 
    <price>99.99</price> 
    </Book> 
    <Book> 
    <sku>54321</sku> 
    <title>Picturescapes</title> 
    <current_inventory>20</current_inventory> 
    <condition>New</condition> 
    <price>50.00</price> 
    </Book> 
    <Book> 
    <sku>33333</sku> 
    <title>Tourist Perspectives</title> 
    <condition>New</condition> 
    <current_inventory>0</current_inventory> 
    <price>75.00</price> 
    </Book> 
    <Book> 
    <sku>10001</sku> 
    <title>Fire in the Sky</title> 
    <condition>Used</condition> 
    <current_inventory>0</current_inventory> 
    <price>10.00</price> 
    </Book> 
</catalog> 

A continuación es mi archivo catalog3.xsl que es capaz de mostrar sólo uno de los dos libros:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 
    <xsl:variable name="max"/> 

    <xsl:template match="/"> 
    <html> 
     <body> 
     <h2>Titles of Books for which Most Copies are Available</h2> 
     <table border="2"> 
      <tr bgcolor="#9acd32"> 
      <th>Title</th> 
      <th>No of Copies</th> 
      </tr> 
      <xsl:apply-templates/> 
     </table> 
     </body> 
    </html> 
    </xsl:template> 

    <xsl:template match="catalog"> 
    <xsl:for-each select="Book"> 
     <xsl:sort select="current_inventory" data-type="number" order="descending"/> 
     <tr> 
     <xsl:if test="position()= 1"> 
      <p><xsl:value-of select="$max = "/></p> 
      <td><xsl:value-of select="title"/></td> 
      <td><xsl:value-of select="current_inventory"/></td> 
     </xsl:if> 
     </tr> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

Podría alguien corregir para lograr mi objetivo de mostrar todas las copias tienen el mismo inventario máximo en el catálogo Gracias.

+0

Buena pregunta: +1. Vea una solución en la que no necesita calcular y mantener el máximo en una variable separada. Además, esta forma de encontrar el máximo puede ser más eficiente que con ordenar todo el conjunto de nodos, ya que solo se ordenan los nodos de diferentes valores. Disfrutar. :) –

Respuesta

2

El máximo current_inventory se puede calcular de la siguiente manera:

<xsl:variable name="max"> 
    <xsl:for-each select="/catalog/Book/current_inventory"> 
    <xsl:sort data-type="number" order="descending"/> 
    <xsl:if test="position()=1"><xsl:value-of select="."/></xsl:if> 
    </xsl:for-each> 
</xsl:variable> 

Ajuste de los criterios para la xsl:if para comparar la current_inventory del nodo actual en el for-each a la variable $max logra el resultado deseado.

Estaba evaluando position()=1 dentro del for-each, que solo será verídico para el primer artículo de la colección ordenada.

me puse a buscar current_inventory que es igual a la $max:

<xsl:if test="current_inventory = $max"> 

La aplicación de estos cambios en su hoja de estilo:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<!--Determine the maximum current_inventory --> 
<xsl:variable name="max"> 
    <xsl:for-each select="/catalog/Book/current_inventory"> 
     <xsl:sort data-type="number" order="descending"/> 
     <xsl:if test="position()=1"><xsl:value-of select="."/></xsl:if> 
    </xsl:for-each> 
</xsl:variable> 

<xsl:template match="/"> 
    <html> 
     <body> 
      <h2>Titles of Books for which Most Copies are Available</h2> 
      <table border="2"> 
       <tr bgcolor="#9acd32"> 
        <th>Title</th> 
        <th>No of Copies</th> 
       </tr> 
       <xsl:apply-templates/> 
      </table> 
     </body> 
    </html> 
</xsl:template> 
<xsl:template match="catalog"> 
    <xsl:for-each select="Book"> 
     <xsl:sort select="current_inventory" data-type="number" order="descending"/> 

     <xsl:if test="current_inventory = $max"> 
          <tr> 
       <td> 
        <xsl:value-of select="title"/> 
       </td> 
       <td> 
        <xsl:value-of select="current_inventory"/> 
       </td> 
          </tr> 
     </xsl:if> 

    </xsl:for-each> 
</xsl:template> 

+0

Hiciste lo imposible ;-) ... una corrección: mueve dentro de Filburt

+0

Estás en lo correcto, he corregido mi respuesta para mover '' dentro de '' –

+0

Mira una solución en la que no es necesario calcular y mantener el máximo en una variable separada. Además, esta forma de encontrar el máximo puede ser más eficiente que con ordenar todo el conjunto de nodos, ya que solo se ordenan los nodos de diferentes valores. Disfrutar. :) –

-2

Ese tipo de cosas no son divertidas con XSL. Para averiguar "si hay dos o más libros del mismo inventario" en el marcado cuando el recuento de inventario puede ser arbitrario requerirá una estructura de datos en la que pueda almacenar un recuento de los inventarios encontrados. Sugeriría atravesar el DOM XML antes de la transformación y contarlo (mucho más fácil), luego crear un nuevo Documento de los nodos que desee transformar y aplicarle el XSL. Eso será más sencillo y menos desordenado que tratar de lograr todo en la hoja de estilos.

+2

Lo que él está pidiendo que haga realmente no es un gran problema en XSLT. Su estructura de datos ** no almacena ** un conteo de inventarios en el elemento 'current_inventory', lo que hace que esto sea trivial. –

+0

Este tipo de tarea es una de las más fáciles para XSLT. Es un buen consejo para cualquiera leer más sobre un tema antes de emitir declaraciones definitivas. –

2

Aquí es una solución utilizando la agrupación Muenchian:

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

<xsl:key name="kBookByNums" 
    match="Book" use="number(current_inventory)"/> 

<xsl:template match="/*"> 
    <xsl:for-each select= 
    "Book 
     [generate-id() 
     = 
     generate-id(key('kBookByNums', 
         number(current_inventory) 
         )[1] 
         ) 
     ] 
    "> 
    <xsl:sort select="current_inventory" 
     data-type="number" order="descending"/> 

     <xsl:if test="position()=1"> 
     <xsl:copy-of select= 
      "key('kBookByNums', 
       number(current_inventory) 
       )"/> 
     </xsl:if> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

Cuando se aplica esta transformación en el documento XML proporcionado, el resultado correcto se produce:

<Book> 
<sku>12345</sku> 
<title>Beauty Secrets</title> 
<condition>New</condition> 
<current_inventory>20</current_inventory> 
<price>99.99</price> 
</Book> 
<Book> 
<sku>54321</sku> 
<title>Picturescapes</title> 
<current_inventory>20</current_inventory> 
<condition>New</condition> 
<price>50.00</price> 
</Book> 

actualización:

Puede parecer que la solución de esta XSL-clave "juega su 'tarjeta de eficiencia' solo para documentos de entrada grandes (es decir, miles de libros). "como alguien lo expresó. De hecho, tal afirmación es bastante imprecisa.

La mejora de la Eficiencia puede ser grande, incluso cuando no hay tantos elementos, si el número de valores diferentes es muy pequeña, en comparación con el número total de valores. Por lo tanto, incluso para conjuntos de elementos bastante pequeños (uno-doscientos), existe un efecto significativo si el número de valores diferentes no es más de 10-20, algo que sucede en muchos casos del mundo real.

Lo que importa es la pequeña proporción de la cantidad de valores diferentes al tamaño total.

+0

Buena solución. Lamentablemente no puedo votar sobre eso. Voté y lo volví a recuperar, ahora el sistema no me deja votar a menos que lo edite; solo realice un pequeño cambio para que la votación vuelva a habilitarse. Gracias. – Tomalak

+0

@Tomalak, me alegra que te haya gustado. Utilicé esta actualización para aclarar la aplicabilidad de la solución y su propia declaración :) –

+1

De acuerdo, tal vez debería haber dicho "cientos" en lugar de "miles". Lo que quiero decir es que un tiempo de procesamiento de, digamos, 50 ms para una solución sin teclas no es realmente distinguible (para un ser humano) de 25 ms que la solución de claves podría tener, incluso si es nominalmente una mejora del 100%. Como siempre, uno debe hacer sus propias mediciones, y no se puede negar que una solución basada en claves es el enfoque más eficiente. ;) – Tomalak

2

Este XSLT 1.0 hoja de estilo

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 
    <xsl:template match="catalog"> 
    <!-- find the maximum <current_inventory> of all books --> 
    <xsl:variable name="MaxInventory"> 
     <xsl:for-each select="Book"> 
     <xsl:sort select="current_inventory" data-type="number" order="descending" /> 
     <xsl:if test="position() = 1"> 
      <xsl:value-of select="current_inventory" /> 
     </xsl:if> 
     </xsl:for-each> 
    </xsl:variable> 

    <!-- output all matching <Book>s, sorted by title --> 
    <table> 
     <xsl:apply-templates select="Book[current_inventory = $MaxInventory]"> 
     <xsl:sort select="title" data-type="text" order="ascending" /> 
     </xsl:apply-templates> 
    </table> 
    </xsl:template> 

    <xsl:template match="Book"> 
    <tr> 
     <td><xsl:value-of select="title" /></td> 
     <td><xsl:value-of select="current_inventory" /></td> 
    </tr> 
    </xsl:template> 

</xsl:stylesheet> 

produce

<table> 
    <tr> 
    <td>Beauty Secrets</td> 
    <td>20</td> 
    </tr> 
    <tr> 
    <td>Picturescapes</td> 
    <td>20</td> 
    </tr> 
</table> 

Nota que existe una solución más eficaz, que implica claves XSL (Dimitre Novatchev lo muestra). Este es sencillo y fácil de seguir, la solución de clave XSL es avanzada y juega su "tarjeta de eficiencia" para documentos de entrada grandes solamente (es decir, miles de libros). Con pequeños documentos de entrada, la diferencia de rendimiento será insignificante.

+0

+1 Buena mejora. –

+0

@Tomalak, la afirmación de que la solución xsl-key -s "reproduce su" tarjeta de eficiencia "para documentos de entrada grandes solamente (es decir, miles de libros)." Es bastante imprecisa. La mejora en la eficiencia puede ser grande incluso cuando no hay tantos elementos, si el número de valores diferentes es muy pequeño, en comparación con el número total de valores. Entonces, incluso para conjuntos bastante pequeños de ítems (uno-doscientos), hay un efecto significativo si el número de valores diferentes no es más de 10-20, algo que sucede en muchos casos del mundo real. –

Cuestiones relacionadas