2009-03-13 13 views
16

私は、オンザフライで生成するXML文書を持っており、重複するノードを排除する関数が必要です。XQueryで重複ノードを削除するにはどうすればよいですか?

のように私の関数が見えます:サンプル出力がある

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

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

そこにパンフレットを言うように、私は私の本のルートタグで一つだけのエントリーをしたい、と他のタグがあります重複を削除する必要があります。何か案は?


以下のコメントが更新されました。ユニークなノードでは、まったく同じ内容と構造を持つ複数のノードが削除されます。

答えて

16

簡単でより直接的なワンライナーのXPathのソリューション

ちょうど次のXPath式を使用します。次のXML文書に、例えば、適用

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

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

上記のXPath式が正しく次のノードを選択:

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

説明は単純です:すべてのbookについては、その発生箇所を1つだけ選択する - などすべての書籍でそのインデックスであることtitleの最初のインデックスと同じの全タイトルにあります。

+0

ちょっとDimitre、答えに感謝;しかし、私が正しく理解すれば、クエリに組み込まれている同じ構造を持つすべての要素に依存します。たとえば、同じタイトルと異なる著者を持つ場合、2つのノードを表示します。 – Brabster

+0

@Brabster不平等/一意性のテストがどのように定義されるべきかという疑問からはっきりとしていません。定義しておけば、より簡単な解決策を見つけるのに役立ちます –

+0

これはXPath 1.0ではうまくいかないようですが、XPath 1.0の実用的なソリューションを手に入れることはできますか? – abarax

1

私は一意性マッチングのために私の文書のテキスト内容だけに基づいて再帰一意性検索機能を実装することで私の問題を解決しました。

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

出力:

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

を、私はあなたがわずかに異なる等価マッチングが必要な場合、あなたはそれに応じたアルゴリズムでマッチングを変更することができますね、次のように呼び出され

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

。あなたはどんな速度でも始めるべきです。

5

あなたはFNについてどう...

+0

どうすれば使えますか? – obesechicken13

1

をビルトインdistinct-values()機能を使用することができます。個別の値を?

2

機能プログラミングに触発された解決策。この解決策は、"="の代わりにカスタムビルドブールlocal:compare($element1, $element2)関数を置き換えることができるという点で拡張可能です。この関数のリストの長さは、最悪の場合 2次複雑度です。リスト手前をソートして直後の後継者と比較するだけで、n(log n)の複雑さを得ることができます。私の最高の知識では

fn:distinct-values(またはfn:distinct-elements)関数は、特注比較機能を使用することはできません。

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) 
+0

この解決策はうまくいくようです。 "fn:exists($ tail [。= $ head])"という行を説明してください。これを "$ head = $ tail"に変更しています。 – abarax

0

あなたはこのfunctx機能を使用することができます。functx:明確な-深い

ありませんが、私は通常、ヘルパー関数を使用して重複を削除するには、ホイールに

1

を改革する必要があります。あなたの場合、それはそのように見えます:

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> 
}; 
関連する問題