XPATH:查找所有具有相同值的元素,直到值发生变化
XPATH: find all elements with same value until the value changes
这是一个示例 XML:
<?xml version="1.0" ?>
<someparent>
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I don't want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Nope, not that one</description>
<id>2</id>
</somechild>
<somechild>
<description>Not that one either</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
</someparent>
<id>
元素始终分组:具有相同 <id>
值的所有元素在文档中彼此跟随。我可能在一个文件中有数千个不同的 <id>s
。我想要的是找到每个 <somechild>
元素,它是其对应 <id>
组的第一个 occurrence。所以我的预期结果是:
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
我需要一个 XPATH 命令来 select 所有这些“组中的第一项”。我尝试了 following-sibling
和 preceding-sibling
轴的各种组合,但我不能完全正确。通过以下声明,我已经非常接近我想要实现的目标:
//someparent/somechild/id[text()=parent::somechild/preceding-sibling::somechild/id[text()]]/parent::somechild
这实际上 returns 我 不 想要的所有节点,因为它 select 是 不想要的所有项目 他们小组中的第一个(所以它基本上是我想要的完美否定!)。但是我这辈子都想不出如何反转结果。
如有任何帮助,我们将不胜感激。
这个O(n2) XPath 1.0表达式,
//someparent/somechild[not(id = preceding-sibling::somechild/id)]
将 select 所有 somechild
没有前面兄弟姐妹的元素具有相同的 id
子元素,
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
根据要求。
更新
Michael Kay 很有帮助的是,上述 XPath 的算法复杂度为 O(n2),因为对于每个子兄弟姐妹,都会比较所有前面的兄弟姐妹。这对少数兄弟姐妹来说无关紧要,但 OP 提到了数千个,因此大小问题成为一个问题。
看他的,这是一个更好的O(n)。
他进一步观察到 O(n) XPath 1.0 表达式是可能的只要只检查紧接在前的兄弟姐妹 :
//someparent/somechild[not(id = preceding-sibling::somechild[1]/id)]
^^^
这种较低复杂度的 XPath 将为 OP 的示例案例产生相同的结果。
一个区分案例将涉及具有 id
值的较晚的兄弟姐妹重复较早的 id
值的集群。例如,添加另一组 id
个具有 98
值的兄弟姐妹:
<someparent>
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I don't want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Nope, not that one</description>
<id>2</id>
</somechild>
<somechild>
<description>Not that one either</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
<somechild>
<description>REPEAT CASE 1</description>
<id>98</id>
</somechild>
<somechild>
<description>REPEAT CASE 2</description>
<id>98</id>
</somechild>
</someparent>
不同之处在于 O(n) XPath 将 不 包含 REPEAT CASE 1
somechild
元素,但是 O(n2) XPath will 包括远距离重复的 REPEAT CASE 1
:
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
<somechild>
<description>REPEAT CASE 1</description>
<id>98</id>
</somechild>
只要需求不需要非立即比较,使用效率更高的O(n) XPath。
在 XPath 3.1 中:
fold-left(//somechild, (), function($z, $i) {
if ($i/id = $z[last()]/id) then $z else ($z, $i)
})
与公认的解决方案不同,这应该具有 O(n) 复杂度(假设 X[last()] 在恒定时间内执行)。
另一种类似于@kjhughes 提出的解决方案的语法:
//id[not(text()=preceding::id/text())]/..
另一个解决方案:
//id[text()!=preceding::id[1]/text() or count(preceding::id)=0]/..
Select id
当前面的第一个 id
值不等于当前 id
的值时。然后selectparent。 Count
用于select第一个somechild
元素的第一个id
。
当然可以用//
代替绝对路径来提高效率
这是一个示例 XML:
<?xml version="1.0" ?>
<someparent>
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I don't want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Nope, not that one</description>
<id>2</id>
</somechild>
<somechild>
<description>Not that one either</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
</someparent>
<id>
元素始终分组:具有相同 <id>
值的所有元素在文档中彼此跟随。我可能在一个文件中有数千个不同的 <id>s
。我想要的是找到每个 <somechild>
元素,它是其对应 <id>
组的第一个 occurrence。所以我的预期结果是:
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
我需要一个 XPATH 命令来 select 所有这些“组中的第一项”。我尝试了 following-sibling
和 preceding-sibling
轴的各种组合,但我不能完全正确。通过以下声明,我已经非常接近我想要实现的目标:
//someparent/somechild/id[text()=parent::somechild/preceding-sibling::somechild/id[text()]]/parent::somechild
这实际上 returns 我 不 想要的所有节点,因为它 select 是 不想要的所有项目 他们小组中的第一个(所以它基本上是我想要的完美否定!)。但是我这辈子都想不出如何反转结果。
如有任何帮助,我们将不胜感激。
这个O(n2) XPath 1.0表达式,
//someparent/somechild[not(id = preceding-sibling::somechild/id)]
将 select 所有 somechild
没有前面兄弟姐妹的元素具有相同的 id
子元素,
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
根据要求。
更新
Michael Kay
看他的
他进一步观察到 O(n) XPath 1.0 表达式是可能的只要只检查紧接在前的兄弟姐妹 :
//someparent/somechild[not(id = preceding-sibling::somechild[1]/id)]
^^^
这种较低复杂度的 XPath 将为 OP 的示例案例产生相同的结果。
一个区分案例将涉及具有 id
值的较晚的兄弟姐妹重复较早的 id
值的集群。例如,添加另一组 id
个具有 98
值的兄弟姐妹:
<someparent>
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I don't want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Nope, not that one</description>
<id>2</id>
</somechild>
<somechild>
<description>Not that one either</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
<somechild>
<description>REPEAT CASE 1</description>
<id>98</id>
</somechild>
<somechild>
<description>REPEAT CASE 2</description>
<id>98</id>
</somechild>
</someparent>
不同之处在于 O(n) XPath 将 不 包含 REPEAT CASE 1
somechild
元素,但是 O(n2) XPath will 包括远距离重复的 REPEAT CASE 1
:
<somechild>
<description>I want this</description>
<id>98</id>
</somechild>
<somechild>
<description>I want this too</description>
<id>2</id>
</somechild>
<somechild>
<description>Yep, I want this</description>
<id>41</id>
</somechild>
<somechild>
<description>REPEAT CASE 1</description>
<id>98</id>
</somechild>
只要需求不需要非立即比较,使用效率更高的O(n) XPath。
在 XPath 3.1 中:
fold-left(//somechild, (), function($z, $i) {
if ($i/id = $z[last()]/id) then $z else ($z, $i)
})
与公认的解决方案不同,这应该具有 O(n) 复杂度(假设 X[last()] 在恒定时间内执行)。
另一种类似于@kjhughes 提出的解决方案的语法:
//id[not(text()=preceding::id/text())]/..
另一个解决方案:
//id[text()!=preceding::id[1]/text() or count(preceding::id)=0]/..
Select id
当前面的第一个 id
值不等于当前 id
的值时。然后selectparent。 Count
用于select第一个somechild
元素的第一个id
。
当然可以用//
代替绝对路径来提高效率