Domanda

Ho un documento XML che ho generato al volo e ho bisogno di una funzione per eliminare eventuali nodi duplicati da esso.

La mia funzione è simile a:

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

L'output di esempio è:

<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>

Voglio solo una voce nel tag root dei miei libri, e ci sono altri tag, come dire anche un opuscolo che devono rimuovere i duplicati. Qualche idea?


Aggiornato dopo i commenti. Per nodi univoci, intendo rimuovere più occorrenze di nodi che hanno lo stesso contenuto e la stessa struttura.

È stato utile?

Soluzione

Una soluzione XPath one-liner più semplice e diretta :

Basta usare la seguente espressione XPath :

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

Se applicato, ad esempio, sul seguente documento XML :

<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'espressione XPath sopra seleziona correttamente i seguenti nodi :

<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>

La spiegazione è semplice: per ogni libro , seleziona solo una delle sue occorrenze, in modo tale che il suo indice in tutti i libri sia lo stesso del primo indice del suo titolo in tutti i titoli .

Altri suggerimenti

È possibile utilizzare la funzione integrata distintivo-valori () ...

Una soluzione ispirata alla programmazione funzionale. Questa soluzione è estensibile in quanto puoi sostituire il " = " con il tuo booleano locale personalizzato: compare ($ element1, $ element2) funzione. Questa funzione presenta il caso peggiore complessità quadratica nella lunghezza dell'elenco. È possibile ottenere la complessità n (log n) ordinando l'elenco in anticipo e confrontandolo solo con il successore immediato.

Per quanto ne so, le funzioni fn: distinti valori (o fn: distinti elementi ) non consentono di utilizzare un personalizzato funzione di confronto.

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)

Ho risolto il mio problema implementando una funzione di ricerca dell'unicità ricorsiva, basata esclusivamente sul contenuto testuale del mio documento per la corrispondenza dell'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
};

Chiamato come segue:

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()

uscita:

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

Suppongo che se hai bisogno di una corrispondenza di equivalenza leggermente diversa, puoi modificare la corrispondenza nell'algoritmo di conseguenza. Dovresti iniziare comunque.

Che dire di fn: valori distinti?

Per rimuovere i duplicati di solito utilizzo una funzione di supporto. Nel tuo caso sembrerà così:

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>
};

È possibile utilizzare questa funzione functx: functx: distinto-profondo

Non è necessario reinventare la ruota

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top