powered by simpleCommunicator - 2.0.50     © 2025 Programmizd 02
Форумы / XML, XSL, XPath, XQuery [игнор отключен] [закрыт для гостей] / XSLT: Двухуровневая группировка
17 сообщений из 17, страница 1 из 1
XSLT: Двухуровневая группировка
    #38277888
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Помогите составить XSLT.

Есть такие исходные данные:
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
<Data>
  <References>
    <Categories>
      <Category name="cat1" title="Категория 1" index="1" />
      <Category name="cat2" title="Категория 2" index="2" />
      <Category name="cat3" title="Категория 3" index="3" />
    </Categories>
    <Groups>
      <Group name="grp1" title="Группа 1" index="3" description="notes 1"/>
      <Group name="grp2" title="Группа 2" index="2" description="notes 2"/>
      <Group name="grp3" title="Группа 3" index="1" description="notes 3"/>
    </Groups>
  </References>
  <Items>
    <Item index="1" name="item1" category="cat1" group="grp1" ... />
    <Item index="2" name="item2" category="cat2" group="grp1" ... />
    <Item index="3" name="item3" category="cat3" group="grp2" ... />
    <Item index="4" name="item4" category="cat3" group="grp2" ... />
  </Items>
</Data>



Мне нужно вывести Items/Item, сгруппировав их вначале по группе, затем по категории. Группы сортируются по индексу группы, категории сортируются по индексу категории, внутри группы элементы сортируются по индексу элемента.
То есть мне нужно получить примерно такую структуру:
Код: html
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<h1>Группа 2</h1>
<p>notes 2</p>

<h2>Категория 3</h2>
<ol>
<li>item3</li>
<li>item4</li>
</ol>

<h1>Группа 1</h1>
<p>notes 1</p>

<h2>Категория 1</h2>
<ol>
<li>item1</li>
</ol>

<h2>Категория 2</h2>
<ol>
<li>item2</li>
</ol>



Как это сделать? В for-each-group можно задавать только один ключ для группировки.

И еще я бы хотел сделать XSLT не в одном общем шаблоне, а в трех шаблона (для заголовка группы, для заголовка категории и для списка элементов), но не соображу, как для шаблона задавать атрибут match.

________________________
Мы смотрим с оптимизмом...
...в оптический прицел.
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38277903
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вот тут я решал похожую проблему: 13107213
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38277907
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
На stackoverflow я нашел несколько примеров с группировкой Мюнха.
Но все примеры исходят из того, что все используемые атрибуты заданы непосредственно для элемента.
У меня же сортировку нужно осуществлять по атрибуту из другого узла.
Можно в крайнем случае сделать два преобразования — первый раз в Item добавить атрибуты group_index и category_index, а вторым проходом группировать по ним — но это как-то неправильно.
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38277914
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Посмотрел внимательнее — абсолютно тоже самое, что у меня. Только названия групп и категорий лежат отдельно.
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38277919
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
AntonariyТолько названия групп и категорий лежат отдельно.
В этом и сложность.
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38278036
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Еще вопрос. В исходных данных я немного неправильно структуру привел.
Правильнее узлы Group выглядят так:
Код: xml
1.
<Group name="grp1" title="Группа 1" index="3">notes 1</Group>



Как мне в XSL получить содержимое узла? Так можно?
Код: xml
1.
<xsl:if test="key('group',@group)/text()">key('group',@group)/text()</xsl:if>
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38278050
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.AntonariyТолько названия групп и категорий лежат отдельно.
В этом и сложность.Выборка из известного раздела по известным условиям? В чем тут сложность?

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
<xsl:key name="item" match="row" use="@group"/>

  <xsl:template match="/Data/Items/*">
    <root>
      <xsl:apply-templates select="*[generate-id(.)=generate-id(key('item',@group))]" mode="key"/>
    </root>
  </xsl:template>

  <xsl:template match="Item" mode="key">
    <h1><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/@title"/></h1>
    <p><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/@description"/></p>      

    <xsl:apply-templates select="parent::*/*[not(@category=preceding-sibling::*/@category)]" mode="category">
        <xsl:sort select="@category" data-type="text" order="ascending" />
        <xsl:with-param name="group" select="@group"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="Item" mode="category">
    <xsl:param name="group"/>
    <h2><xsl:value-of select ="/Data/References/Categories/*[@name=current()/@category]/@title"/></h2>
    <ol><xsl:apply-templates select="parent::*/*[@group=$group][@category=current()/@category]" mode="category"/></ol>
  </xsl:template>

  <xsl:template match="row" mode="category">
    <li><xsl:value-of select="@name"/></li>
  </xsl:template>
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38278058
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.Еще вопрос. В исходных данных я немного неправильно структуру привел.
Правильнее узлы Group выглядят так:
Код: xml
1.
<Group name="grp1" title="Группа 1" index="3">notes 1</Group>


Код: xml
1.
<p><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/text()"/></p>
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38278065
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ошибочка.
Код: xml
1.
<xsl:key name="item" match="Item" use="@group"/>
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38278077
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Еще ошибки нашел. Вот окончательный вариант:
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
<xsl:key name="item" match="Item" use="@group"/>

  <xsl:template match="/Data/Items/*">
    <root>
      <xsl:apply-templates select="*[generate-id(.)=generate-id(key('item',@group))]" mode="key"/>
    </root>
  </xsl:template>

  <xsl:template match="Item" mode="key">
    <h1><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/@title"/></h1>
    <p><xsl:value-of select ="/Data/References/Groups/*[@name=current()/@group]/text()"/></p>

    <xsl:apply-templates select="parent::*/*[not(@category=preceding-sibling::*/@category)]" mode="categorylist">
        <xsl:sort select="@category" data-type="text" order="ascending" />
        <xsl:with-param name="group" select="@group"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="Item" mode="categorylist">
    <xsl:param name="group"/>
    <h2><xsl:value-of select ="/Data/References/Categories/*[@name=current()/@category]/@title"/></h2>
    <ol><xsl:apply-templates select="parent::*/*[@group=$group][@category=current()/@category]" mode="category"/></ol>
  </xsl:template>

  <xsl:template match="Item" mode="category">
    <li><xsl:value-of select="@name"/></li>
  </xsl:template>
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38278100
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сделал вот так:
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
...
<xsl:key name="group"  match="/Data/References/Groups/Group" use="@name"/>
<xsl:key name="category" match="/Data/References/Categories/Category" use="@name"/>
<xsl:key name="item_group"  match="/Data/Items/Item" use="@group"/>
<xsl:key name="item_category"  match="/Data/Items/Item" use="concat(@group,'|',@category)"/>
...

<table>

<xsl:for-each select="//Items/Item[generate-id() = generate-id(key('item_group',@group)[1])]">
<xsl:sort select="key('group',@group)/@index"/>
<!-- group GROUP -->
<tr>
<td colspan="3">
<h2"><xsl:value-of select="key('group',@group)/@name"/></h2>
<xsl:if test="key('group',@group)/text()"><xsl:value-of select="key('group',@group)/text()"/></xsl:if>
</td>
</tr>
<!-- /group GROUP -->

<xsl:for-each select="key('item_group',@group)[generate-id() = generate-id(key('item_category',concat(@group,'|',@category))[1])]">
<xsl:sort select="key('category',@category)/@index"/>
<!-- CATEGORY GROUP -->
<tr>
<td colspan="3"><h3"><xsl:value-of select="key('category',@category)/@title"/></h3></td>
</tr>
<!-- /CATEGORY GROUP -->

<xsl:for-each select="key('item_category',concat(@group,'|',@category))">
<xsl:sort select="@index"/>
<!-- ITEM LIST -->
<tr>
<td><img src="..."/></td>
<td><span><xsl:value-of select="@title"/></span><span>#<xsl:value-of select="@index"/></span><br/>
<xsl:if test="text()"><xsl:value-of select="text()"/></xsl:if><xsl:if test="not(text())"><span style="color:gray;font-style:oblique;">без описания</span></xsl:if></td>
<td>etc.</td>
</tr>
<!-- /ITEM LIST-->
</xsl:for-each>

</xsl:for-each>

</xsl:for-each>

</table>



В целом структура примерно такая, какая мне нужна.
Однако внутри группы элементы почему-то не сортируются.
Что нужно исправить?
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38278107
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Antonariy<xsl:param name="group"/>
Действительно, про параметры я забыл.
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38278116
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.Что нужно исправить?
Разобрался, нужно было числовой тип данных указать (data-type="number").
Он у меня сортировал @index как текст.
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38278145
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Еще вопрос, можно ли пронумеровать все элементы (сквозная нумерация)?
position() не подходит, он начинает нумерацию с начала в каждой группе.
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38280113
mage.lan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.,

зачем для такой простой задачи, такое сложное решение? Как описали, так и надо решать.
Что касается нумерации, если нет избыточных данных, то можно сделать, если есть... будет нетривиально, с помощью рекурсии, либо с помощью двойной трансформации, итемы набираются в переменную, там где надо вставить группы/категории у итемов прописывается что-то, потом все гонится в один for на выход с проверкой, не надо ли порубить OL с помощью CDATA.

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
	<xsl:output method="html" indent="yes" />

	<xsl:template match="/">
		<xsl:variable name="items" select="/Data/Items/Item" />
		<xsl:for-each select="/Data/References/Groups/Group">
			<xsl:sort select="@index" data-type="number"/>
			<xsl:variable name="group" select="." />
			<xsl:for-each select="/Data/References/Categories/Category">
				<xsl:sort select="@index" data-type="number"/>
				<xsl:variable name="category" select="." />
				<xsl:variable name="set" select="$items[@category=$category/@name and @group=$group/@name]" />
				<xsl:if test="count($set)>0">
					<H1>
						<xsl:value-of select="$group/@title" />
					</H1>
					<H2>
						<xsl:value-of select="$category/@title" />
					</H2>
					<OL>
						<xsl:for-each select="$set">
							<LI>
								<xsl:value-of select="@name" />
							</LI>
						</xsl:for-each>
					</OL>
				</xsl:if>
			</xsl:for-each>
		</xsl:for-each>
	</xsl:template>
</xsl:stylesheet>
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38280246
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mage.lan , про такой способ я тоже думал.
Но у него есть два недостатка. А в предложенной реализации даже три.
1. В предложенной реализации заголовок группы выводится для каждой категории. Мне заголовок группы нужно выводить только один раз, то есть этот фрагмент нужно переместить во внешний цикл.
2. Могут выводится пустые заголовки. В предложенной реализации считается число узлов, соответствующих критерию, чтобы не выводить пустой заголовок. Но с учетом п.1 такую проверку нужно делать дважды.
3. Число записей у меня ожидается достаточно большое (минимум несколько сотен). При таких значениях key будет гораздо быстрее, чем вычисление $items[@category=$category/@name and @group=$group/@name] на каждом проходе. И кстати, разве в XSLT1 можно использовать переменную для ссылки на узел? Помоему там были какие-то сложности.
...
Рейтинг: 0 / 0
XSLT: Двухуровневая группировка
    #38280609
mage.lan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.,

1) не проблема, в первом цикле заводим переменную pos = position()
вокруг h1 делаем проверку на $pos = 1, нет проблемы.

2) не нужно делать проверку дважды, вторая проверка будет делаться внутри

3) приведенный код жрет любой парсер, даже браузерный, проблемы с переменными в другом.
И еще, ваша реализация с ключами и то, как вы их используете, медленнее. Парсеры оптимизированы на простые выборки. Не верите - проверяйте. На 1000 записей вы вообще разницы не увидите в скорости.

Ну и еще, когда вы через 2 месяца полезете править свой код, волосы на голове у вас начнут шевелится, т.к. вы вообще не поймете, что там было написано, в поддержке мой вариант как бы тоже предпочтительнее.
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / XML, XSL, XPath, XQuery [игнор отключен] [закрыт для гостей] / XSLT: Двухуровневая группировка
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]