Find all nodes that have an attribute that matches

2019-03-18 13:27发布

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

5条回答
劫难
2楼-- · 2019-03-18 13:43

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).

查看更多
Lonely孤独者°
3楼-- · 2019-03-18 13:47
def nodeHasValue(node:Node,value:String) = node.attributes.value != null && node.attributes.value.contains(value)

(x \\ "_").filter( nodeHasValue(_,"test"))
查看更多
Anthone
4楼-- · 2019-03-18 13:59

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>)
查看更多
趁早两清
5楼-- · 2019-03-18 14:04

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.

查看更多
甜甜的少女心
6楼-- · 2019-03-18 14:05

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"

查看更多
登录 后发表回答