12

I saw the following example on Nabble, where the goal was to return all nodes that contain an attribute with an id of X that contains a value Y:

//find all nodes with an attribute "class" that contains the value "test" val xml = XML.loadString( """<div> <span class="test">hello</span> <div class="test"><p>hello</p></div> </div>""" ) def attributeEquals(name: String, value: String)(node: Node) = { node.attribute(name).filter(_==value).isDefined } val testResults = (xml \\ "_").filter(attributeEquals("class","test")) //prints: ArrayBuffer( //<span class="test">hello</span>, //<div class="test"><p>hello</p></div> //) println("testResults: " + testResults ) 

As an extension to this how would one do the following: Find all nodes that contain any attribute that contains a value of Y:

//find all nodes with any attribute that contains the value "test" val xml = XML.loadString( """<div> <span class="test">hello</span> <div id="test"><p>hello</p></div> <random any="test"/></div>""" ) //should return: ArrayBuffer( //<span class="test">hello</span>, //<div id="test"><p>hello</p></div>, //<random any="test"/> ) 

I was thinking I could use a _ like so:

val testResults = (xml \\ "_").filter(attributeEquals("_","test")) 

But it doesn't work. I know I can use pattern matching, but just wanted to see if I could do some magic with the filtering.

Cheers - Ed

1

5 Answers 5

22

First, XML are literals in Scala, so:

val xml = <div><span class="test">hello</span><div class="test"><p>hello</p></div></div> 

Now, as to the problem:

def attributeValueEquals(value: String)(node: Node) = { node.attributes.exists(_.value.text == value) } 

In fact, I'd have used "exists" instead of "filter" and "defined" for the original problem as well.

Finally, I personally prefer operator style syntax, particularly when you have a ready function, instead of an anonymous one, to pass to "filter":

val testResults = xml \\ "_" filter attributeValueEquals("test") 

The original mixes operator style for "\\" and dot style for "filter", which ends up quite ugly.

Sign up to request clarification or add additional context in comments.

1 Comment

if you are using scala 2.9.1, please see this answer stackoverflow.com/questions/3819485/… (this selected answer returns a NodeSeq() on 2.9.1
3

Code snippet in the question doesn't working with Scala 2.8 - due to this comparasion

(_ == value)
Needs to be replaced with (_.text == value) or (_ == Text(value)) or change type of value from String to Text.

And in Daniel's answer (_.value == value) needs to be replaced with (_.value.text == value).

Comments

3

The previous solutions didn't work for me because they all look for any value that matches. If you want to look for a particular attribute with a value, here is my solution:

def getByAtt(e: Elem, att: String, value: String) = { def filterAtribute(node: Node, att: String, value: String) = (node \ ("@" + att)).text == value e \\ "_" filter { n=> filterAtribute(n, att, value)} } 

And then

getByAtt(xml, "class", "test") 

This will differentiate between class="test" and "notclass="test"

Comments

0

I'm quite new to Scala, I propose you this solution, but I'm not sure this is the best one:

def attributeValueEquals(value: String)(node: Node) = { node.attributes.foldLeft(false)((a : Boolean, x : MetaData) => a | (x.value == value)) } val testResults = (xml \\ "_").filter(attributeValueEquals("test")) println("testResults: " + testResults ) // prints: testResults: ArrayBuffer(<span class="test">hello</span>, // <div id="test"><p>hello</p></div>, // <random any="test"></random>) 

Comments

0
def nodeHasValue(node:Node,value:String) = node.attributes.value != null && node.attributes.value.contains(value) (x \\ "_").filter( nodeHasValue(_,"test")) 

2 Comments

I would suggest maxme's answer for the currying, but my answer for the use of 'contains' rather than a fold. Combine them!
Well, Daniel's answer beats both. Kudos.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.