![]() |
Александр Самиляк
7 июля 2011 |
|
![]() |
Задача. | Разобрать по косточкам теорию и практику использования временных деревьев в XSLT. |
![]() |
![]() |
![]() |
Случается, что на XSL падает задача сложной обработки входящего XML, скажем группировки и сортировки. Пример такой задачи рассматривал Дима Филатов в своей статье про алфавитные указатели. Напомню, что там в качестве метода группировки фамилий по первой букве была применена группировка Мюнха, довольно непростой для понимания и чтения метод. Решим ту же самую задачу с помощью многоступенчатой трансформации.
Итак, на входе у нас есть XML вида:
<list> <item>Орлова</item> <item>Владимирова</item> <item>Якушева</item> <item>Владин</item> <item>Александров</item> ... </list>
Требуется сгруппировать все фамилии по первой букве, отсортировать группы по алфавиту и равномерно распределить группы по n колонкам.
Шаг первый — сортируем фамилии и сохраняем плоским списком:
<xsl:variable name="sorted_plain"> <xsl:for-each select="/list/item"> <xsl:sort select="." /> <xsl:copy-of select="." /> </xsl:for-each> </xsl:variable>
Теперь в переменной $sorted_plain хранится временное дерево вида:
<item>Александров</item> <item>Алферова</item> <item>Бутыркина</item> <item>Владимирова</item> <item>Владин</item> ...
Шаг второй — группируем фамилии по первой букве:
<xsl:variable name="sorted_and_grouped"> <xsl:apply-templates select="exsl:node-set($sorted_plain)/item[1]" mode="items_grouping" /> </xsl:variable> <xsl:template match="item" mode="items_grouping"> <xsl:variable name="first_letter" select="substring(., 1, 1)" /> <xsl:variable name="items_with_this_letter" select="../item[substring(., 1, 1) = $first_letter]" /> <group letter="{$first_letter}"> <xsl:copy-of select="$items_with_this_letter" /> </group> <!-- Входящий список в $sorted_plain отсортирован, поэтому в следующую группу отправляем элемент, идущий за последним элементом в текущей группе --> <xsl:apply-templates select="$items_with_this_letter[last()]/following-sibling::item[1]" mode="items_grouping" /> </xsl:template>
Сейчас в переменной $sorted_and_grouped находится новое временное дерево, содержащее отсортированные группы:
<group letter="А"> <item>Александров</item> <item>Алферова</item> </group> <group letter="Б"> <item>Бутыркина</item> </group> ...
Вот так в два простых приема из неудобного входящего
Шаг третий — равномерно распределяем группы по n колонкам-дивам. Это можно сделать, например, так:
<xsl:variable name="COLS" select="4" /> <xsl:variable name="groups" select="exsl:node-set($sorted_and_grouped)/group" /> <xsl:variable name="count_of_groups_in_one_col" select="ceiling(count($groups) div $COLS)" /> <!-- Импровизированный цикл по счетчику, основанный на предположении о том, что число колонок меньше, чем количество элементов, распределяемых по этим колонкам --> <xsl:for-each select="$groups[position() <= $COLS]"> <xsl:variable name="i" select="position()" /> <div class="col col_{$i}"> <xsl:for-each select="$groups[ position() > $count_of_groups_in_one_col * ($i - 1) and position() <= $count_of_groups_in_one_col * $i ]"> <div class="group"> <h3> <xsl:value-of select="@letter" /> </h3> <ul> <xsl:for-each select="item"> <li> <xsl:value-of select="." /> </li> </xsl:for-each> </ul> </div> </xsl:for-each> </div> </xsl:for-each>
На выходе получаем такой HTML:
<div class="col col_1"> <div class="group"> <h3>А</h3> <ul><li>Александров</li><li>Алферова</li></ul> </div> <div class="group"> <h3>Б</h3> <ul><li>Бутыркина</li></ul> </div> ... </div> <div class="col col_2"> <div class="group"> <h3>Г</h3> <ul><li>Гомиашвили</li><li>Гончар</li><li>Гусев</li></ul> </div> ... </div> <div class="col col_3"> <div class="group"> <h3>Л</h3> <ul><li>Ломов</li></ul> </div> ... </div> <div class="col col_4"> <div class="group"> <h3>Ф</h3> <ul><li>Феоктистов</li><li>Фролов</li></ul> </div> ... </div>
Добавить щепотку CSS — и наш алфавитный указатель готов.
Таким образом, суть многоступенчатой трансформации заключается в том, что на каждом шаге промежуточные результаты расчетов, сортировок и перегруппировок сохраняются во временное дерево, которое затем служит простой основой для следующего шага.
Это было последнее применение временных деревьев, о котором я хотел рассказать. В следующей части мы поговорим об XSL-трансформаторах.
© 19952025 Студия Артемия Лебедева
|