给定一个scala.xml.Node
对象(空格和元素作为子节点)获取第二个(或第n个)子元素的最有效方法是什么?
通常我会选择内置(node \ "foo")
,但有时我必须依赖元素的位置。例如,我可以有两个选择组,可以是foo
或bar
。该文件可能是
<something>
<foo/>
<foo/>
</something>
或
<something>
<foo/>
<bar/>
</something>
等
答案 0 :(得分:3)
我喜欢反义词的drop(n).headOption
模式,因为当你的孩子少于n
时,它就会出现问题。但我认为你的意思是第二个子节点(不包括文本节点),而不是<foo>
标签的第二个实例。考虑到这一点,结合您的答案或使用partialMap
:
node.child.partialMap{case x:scala.xml.Elem => x}.drop(n).headOption
node.child.filter(_.isInstanceOf[scala.xml.Elem]).drop(n).headOption
这必须假设您不想在:
中提取文本val node = <something><foo/>text</something>
效率明智,我能想到的唯一另一件事就是如果你想在有大量孩子的情况下检索第二个孩子,那就是使过滤器变得懒惰。我认为这可以通过在node.child.iterator
上运行过滤器来实现。
修改强>
已将toIterable
更改为iterator
。
好的,在drop(n)
上调用ArrayBuffer
会导致额外的分配,也会有多少分配,因为在drop
中似乎会覆盖IndexSeqLike
。但是使用迭代器也可以解决这个问题。所以对于大量的孩子:
node.child.iterator.filter(_.isInstanceOf[scala.xml.Elem]).drop(n).next
如果您想让它安全,您可能需要定义一个功能来检查hasNext
。
所有这些仅在2.8中进行测试。
答案 1 :(得分:2)
获取名为“foo”的第二个元素,如果未找到,则获取None
:
(xml \ "foo").drop(1).headOption
或者,在大型XML结构的情况下更有效:
xml.child.toStream.partialMap {
case e: xml.Elem if e.label == "foo" => e
}.drop(1).headOption
(这是Scala 2.8)
<强>更新强>
获得第二个,无论名称如何:
(xml \ "_") drop(1) headOption
答案 2 :(得分:1)
到目前为止我所拥有的是:
node.child.filter(_.isInstanceOf[scala.xml.Elem])(1)