Question

Je génère un document XML à la volée et j'ai besoin d'une fonction pour en éliminer les nœuds en double.

Ma fonction ressemble à:

declare function local:start2() {
    let $data := local:scan_books()
    return <books>{$data}</books>
};

Exemple de sortie:

<books>
  <book>
    <title>XML in 24 hours</title>
    <author>Some Guy</author>  
  </book>
  <book>
    <title>XML in 24 hours</title>
    <author>Some Guy</author>  
  </book>
</books>

Je ne veux qu'une entrée dans la balise racine de mes livres, et il existe d'autres balises, comme par exemple une brochure dans celle-ci, pour lesquelles il est nécessaire de supprimer les doublons. Des idées?

Mise à jour des commentaires suivants. Par nœuds uniques, j'entends supprimer plusieurs occurrences de nœuds ayant exactement le même contenu et la même structure.

Était-ce utile?

La solution

Une solution XPath one-liner plus simple et plus directe :

Utilisez simplement l'expression XPath suivante :

  /*/book
        [index-of(/*/book/title, 
                  title
                 )
                  [1]
        ]

Lorsqu'il est appliqué, par exemple, sur le document XML suivant :

<books>
    <book>
        <title>XML in 24 hours</title>
        <author>Some Guy</author>
    </book>
    <book>
        <title>Food in Seattle</title>
        <author>Some Guy2</author>
    </book>
    <book>
        <title>XML in 24 hours</title>
        <author>Some Guy</author>
    </book>
    <book>
        <title>Food in Seattle</title>
        <author>Some Guy2</author>
    </book>
    <book>
        <title>How to solve XPAth Problems</title>
        <author>Me</author>
    </book>
</books>

l'expression XPath ci-dessus sélectionne correctement les nœuds suivants :

<book>
    <title>XML in 24 hours</title>
    <author>Some Guy</author>
</book>
<book>
    <title>Food in Seattle</title>
    <author>Some Guy2</author>
</book>
<book>
    <title>How to solve XPAth Problems</title>
    <author>Me</author>
</book>

L’explication est simple: pour chaque livre , sélectionnez une seule de ses occurrences, de sorte que son index dans tous les livres soit identique au premier index de son titre dans tous les titres .

Autres conseils

Vous pouvez utiliser la fonction distinct-values ??() intégrée ...

Une solution inspirée de la programmation fonctionnelle. Cette solution est extensible en ce sens que vous pouvez remplacer la comparaison " = = par votre code personnalisé booléen local: compare ($ element1, $ element2) fonction. Cette fonction présente une complexité quadratique dans le pire des cas dans la longueur de la liste. Vous pouvez obtenir la complexité de n (log n) en triant la liste à l'avance et en ne comparant qu'avec le successeur immédiat.

À ma connaissance, les fonctions fn: valeurs distinctes (ou fn: éléments distincts ) ne permettent pas d'utiliser une propriété personnalisée fonction de comparaison.

declare function local:deduplicate($list) {
  if (fn:empty($list)) then ()
  else 
    let $head := $list[1],
      $tail := $list[position() > 1]
    return
      if (fn:exists($tail[ . = $head ])) then local:deduplicate($tail)
      else ($head, local:deduplicate($tail))
};

let $list := (1,2,3,4,1,2,1) return local:deduplicate($list)

J'ai résolu mon problème en implémentant une fonction de recherche récursive d'unicité, basée uniquement sur le contenu textuel de mon document pour la correspondance d'unicité.

declare function ssd:unique-elements($list, $rules, $unique) {
    let $element := subsequence($rules, 1, 1)
    let $return :=
    if ($element) then
        if (index-of($list, $element) >= 1) then
            ssd:unique-elements(insert-before($element, 1, $list), subsequence($rules, 2), $unique)
        else <test>
            <unique>{$element}</unique>
            {ssd:unique-elements(insert-before($element, 1, $list), subsequence($rules, 2), insert-before($element, 1, $unique))/*}
            </test>
    else ()
    return $return
};

Appelé comme suit:

declare function ssd:start2() {
    let $data := ()
    let $sift-this := 
       <test>
           <data>123</data>
           <data>456</data>
           <data>123</data>
           <data>456</data>
           <more-data>456</more-data>
       </test>
    return ssd:unique-elements($data, $sift-this/*, ())/*/*
};

ssd:start2()

sortie:

<?xml version="1.0" encoding="UTF-8"?>
<data>123</data>
<data>456</data>

Je suppose que si vous avez besoin d'une correspondance légèrement différente, vous pouvez modifier la correspondance dans l'algorithme en conséquence. De toute façon, vous devriez commencer.

Qu'en est-il de fn: distinct-values?

Pour supprimer les doublons, j'utilise généralement une fonction d'assistance. Dans votre cas, ça va ressembler à ça:

declare function local:remove-duplicates($items as item()*) 
as item()*
{
  for $i in $items
  group by $i
    return $items[index-of($items, $i)[1]]
};

declare function local:start2() {
    let $data := local:scan_books()
    return <books>{local:remove-duplicates($data)}</books>
};

Vous pouvez utiliser cette fonction functx: functx: distinct-deep

Pas besoin de réinventer la roue

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top