我有一个动态生成的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>
我只想要我的书籍根标签中的一个条目,还有其他标签,比如说那里的小册子也需要删除重复项。有什么想法吗?
更新了以下评论。通过唯一节点,我的意思是删除多个具有完全相同内容和结构的节点。
答案 0 :(得分:16)
更简单,更直接的单行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
,只选择其中一个出现 - 以使其 all-books 中的索引与 all-titles 中的title
的第一个索引。
答案 1 :(得分:5)
您可以使用内置的distinct-values()
功能...
答案 2 :(得分:2)
受功能编程启发的解决方案。此解决方案是可扩展的,您可以使用自定义构建的布尔local:compare($element1, $element2)
函数替换“=”比较。此函数在列表长度上具有最坏情况二次复杂度。您可以通过对列表事前进行排序并仅与直接后继者进行比较来获得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)
答案 3 :(得分:1)
我通过实现递归唯一性搜索功能解决了我的问题,该功能完全基于我的文档的文本内容进行唯一性匹配。
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>
我想如果你需要稍微不同的等价匹配,你可以相应地改变算法中的匹配。应该让你开始无论如何。
答案 4 :(得分:1)
fn:distinct-values怎么样?
答案 5 :(得分: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>
};
答案 6 :(得分:0)
您可以使用此功能函数:functx:distinct-deep
无需重新发明轮子