Scala: Учитывая scala.xml.Node, каков наиболее эффективный способ получения второго (или n-го) дочернего элемента? - PullRequest
2 голосов
/ 18 февраля 2010

С учетом объекта scala.xml.Node (с пробелами и элементами в качестве дочерних узлов), каков наиболее эффективный способ получения второго (или n-го) дочернего элемента?

Обычно я бы выбрал встроенный (node \ "foo"), но иногда мне приходится полагаться на положение элемента. Например, у меня может быть две группы выбора, которые могут быть либо foo, либо bar. Документ может быть

<something>
  <foo/>
  <foo/>
</something>

или

<something>
  <foo/>
  <bar/>
</something>

и т.д.

Ответы [ 3 ]

3 голосов
/ 18 февраля 2010

Мне нравится шаблон 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.

Edit: Изменено toIterable на iterator. Хорошо, что вызов drop(n) для ArrayBuffer вызовет дополнительные выделения, а также, сколько из них трудно сказать, так как кажется, что drop переопределено в IndexSeqLike. Но использование итератора также решит эту проблему. Так что для большого количества детей:

node.child.iterator.filter(_.isInstanceOf[scala.xml.Elem]).drop(n).next

Если вы хотите, чтобы это было безопасно, вам может потребоваться определить функцию для проверки на hasNext.

Все это проверено только в 2.8.

2 голосов
/ 18 февраля 2010

Получить второй элемент с именем "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
1 голос
/ 18 февраля 2010

То, что у меня есть, это:

node.child.filter(_.isInstanceOf[scala.xml.Elem])(1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...