Как удалить столбец с таким же содержимым? - PullRequest
0 голосов
/ 25 января 2020

Мне нужно удалить и идентифицировать столбец с одинаковыми значениями. Значение XML выглядит примерно так:

<PRODTABLE>
    <PRODTR>
        <PRODTD>H1</PRODTD>
        <PRODTD>H2</PRODTD>
        <PRODTD>H3</PRODTD>
        <PRODTD>Headline 6</PRODTD>
        <PRODTD blacklist="1">Head 7</PRODTD>
        <PRODTD>Head 8</PRODTD>
        <PRODTD blacklist="1">Test</PRODTD>
    </PRODTR>
    <PRODTR>
        <PRODTD>7.5</PRODTD>
        <PRODTD>95mm</PRODTD>
        <PRODTD>WHATEVERTEXT</PRODTD>
        <PRODTD>Lorem</PRODTD>
        <PRODTD blacklist="1">Foobar</PRODTD>
        <PRODTD>TEST</PRODTD>
        <PRODTD blacklist="1">011601100</PRODTD>
    </PRODTR>
    <PRODTR>
        <PRODTD>9</PRODTD>
        <PRODTD>92mm</PRODTD>
        <PRODTD>WHATEVERTEXT</PRODTD>
        <PRODTD>Lorem</PRODTD>
        <PRODTD blacklist="1">Foobar</PRODTD>
        <PRODTD>TEST</PRODTD>
        <PRODTD blacklist="1">021063100</PRODTD>
    </PRODTR>
</PRODTABLE>

После запуска через мою таблицу стилей должно получиться следующее:

    <PRODTABLE>
    <PRODTR>
        <PRODTD>H1</PRODTD>
        <PRODTD>H2</PRODTD>
        <PRODTD blacklist="1">Head 7</PRODTD>
        <PRODTD blacklist="1">Test</PRODTD>
    </PRODTR>
    <PRODTR>
        <PRODTD>7.5</PRODTD>
        <PRODTD>95mm</PRODTD>
        <PRODTD blacklist="1">Foobar</PRODTD>
        <PRODTD blacklist="1">011601100</PRODTD>
    </PRODTR>
    <PRODTR>
        <PRODTD>9</PRODTD>
        <PRODTD>92mm</PRODTD>
        <PRODTD blacklist="1">Foobar</PRODTD>
        <PRODTD blacklist="1">021063100</PRODTD>
    </PRODTR>
</PRODTABLE>

Таким образом, все равные ячейки в столбце удаляются. (см. WHATEVERTEXT, Lorem, TEST от источника XML). Мне уже удалось исключить столбцы, занесенные в черный список. Столбцы из черного списка должны (даже если они могут быть равны) игнорироваться.

Это мой пустой шаблон для сопоставления со всеми дубликатами и просто ничего с ним не делать.

<xsl:template match="PRODTD[ not(
    for $c in count(preceding-sibling::PRODTD)+1 return
      ../../PRODTR/PRODTD[$c][. = //*/PRODTR[2]/PRODTD[$c]]) and
    @blacklist != 1]"/>

Это печатает результат для всех оставшихся или занесенных в черный список ячеек.

<xsl:template match="PRODTD[ 
    for $c in count(preceding-sibling::PRODTD)+1 return
      ../../PRODTR/PRODTD[$c][. = //*/PRODTR[2]/PRODTD[$c]] or
    @blacklist = 1]">

    [...]

</xsl:template>

Мой текущий текущий результат распечатывает только занесенные в черный список столбцы.

Я уже использовал это решение xslt удалить столбец таблицы, когда все указано c значение , но ему не удалось настроить его так, чтобы он соответствовал всем другим записям столбцов вместо '-'.

Подсказка: до и после этой таблицы PRODTABLE может содержать больше XML элементов.

Ответы [ 2 ]

2 голосов
/ 25 января 2020

Я думаю, что ключ помогает, в XSLT 3 (поддерживается с. NET и Java с помощью Saxon 9.8 HE или выше) вы можете использовать составной ключ:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:key name="col-index-value"
    match="PRODTABLE/PRODTR[position() gt 1]/PRODTD[not(@blacklist = 1)]"
    composite="yes"
    >
      <xsl:variable name="pos" as="xs:integer">
          <xsl:number/>
      </xsl:variable>
      <xsl:sequence select="$pos, string()"/>
  </xsl:key>

  <xsl:template match="PRODTABLE/PRODTR[1]/PRODTD[let $pos := position() return count(key('col-index-value', (position(), ../../PRODTR[2]/PRODTD[$pos]), ancestor::PRODTABLE)) = (count(ancestor::PRODTABLE/PRODTR) - 1)]"/>
  <xsl:template 
    match="PRODTABLE/PRODTR[position() gt 1]/PRODTD[not(@blacklist = 1) and count(key('col-index-value', (position(), string()), ancestor::PRODTABLE)) = (count(ancestor::PRODTABLE/PRODTR) - 1)]"/>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/6rexjhU

Или достаточно просто вычислить и подсчитать различные значения и, основываясь на этом числе, передать в качестве параметра туннеля, исключить col:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="PRODTABLE">
      <xsl:copy>
          <xsl:variable name="count-col-values"
            select="let $rows := PRODTR[position() > 1]
                    return
                    for $pos in 1 to count($rows[1]/PRODTD)
                    return count(distinct-values($rows/PRODTD[$pos]))"/>
          <xsl:apply-templates>
              <xsl:with-param name="count-col-values" select="$count-col-values" tunnel="yes"/>
          </xsl:apply-templates>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="PRODTD[not(@blacklist = 1)]">
      <xsl:param name="count-col-values" tunnel="yes"/>
      <xsl:if test="subsequence($count-col-values, position(), 1) > 1">
          <xsl:next-match/>
      </xsl:if>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/6rexjhU/1

Кажется, что можно также упростить простую передачу индексов столбцов столбцов, не имеющих одинаковое значение:

  <xsl:template match="PRODTABLE">
      <xsl:copy>
          <xsl:variable name="positions"
            select="let $rows := PRODTR[position() > 1]
                    return
                    for $pos in 1 to count($rows[1]/PRODTD)
                    return $pos[count(distinct-values($rows/PRODTD[$pos])) > 1]"/>
          <xsl:apply-templates>
              <xsl:with-param name="positions" select="$positions" tunnel="yes"/>
          </xsl:apply-templates>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="PRODTD[not(@blacklist = 1)]">
      <xsl:param name="positions" tunnel="yes"/>
      <xsl:if test="$positions = position()">
          <xsl:next-match/>
      </xsl:if>
  </xsl:template>

https://xsltfiddle.liberty-development.net/6rexjhU/2

0 голосов
/ 25 января 2020

Другой взгляд на это:

XSLT 2.0

<xsl:key name="col" match="PRODTD" use="index-of(../PRODTD, .)" />

<xsl:template match="PRODTD[not(.!=key('col', index-of(../PRODTD, .)))]"/>

Демонстрация: https://xsltfiddle.liberty-development.net/bFWRAoY


Это можно сделать более эффективным, запустив тест только для ячеек первой строки и сохранив результаты в переменной, а затем воспользовавшись этим, чтобы исключить ячейки из номеров столбцов, хранящихся в переменной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...