XQueryで重複ノードを削除するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/644384

  •  22-07-2019
  •  | 
  •  

質問

オンザフライで生成するXMLドキュメントがあり、重複するノードを削除する関数が必要です。

私の機能は次のようになります:

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>

書籍のルートタグには1つのエントリのみが必要です。また、そこにあるパンフレットなど、重複を削除する必要があるタグもあります。アイデアはありますか?


次のコメントを更新しました。一意のノードとは、まったく同じコンテンツと構造を持つノードの複数の出現を削除することを意味します。

役に立ちましたか?

解決

よりシンプルで直接的なワンライナーXPathソリューション

次のXPath式を使用してください

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

たとえば、次の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>

上記の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つだけ選択します。たとえば、 all-books のインデックスは all-titles title の最初のインデックスと同じ。

他のヒント

組み込みの distinct-values()関数を使用できます...

関数型プログラミングに触発されたソリューション。このソリューションは、&quot; =&quot; の比較をカスタムビルドブール型 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)

一意性の一致のためにドキュメントのテキストコンテンツのみに基づいて、再帰的な一意性検索機能を実装することで問題を解決しました。

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

次のように呼び出されます:

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>

わずかに異なる等価マッチングが必要な場合は、それに応じてアルゴリズムのマッチングを変更できます。とにかく始めるべきです。

fn:distinct-valuesはどうですか?

重複を削除するには、通常ヘルパー関数を使用します。あなたの場合、それは次のようになります:

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

このfunctx関数を使用できます:functx:distinct-deep

車輪を再発明する必要はありません

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top