views:

69

answers:

1

Building on this this post, I needed a clean way to extract nodes in a for comprehension only if they had specific attribute values. Here's what I came up with:

def attributeValue(attrs: (String, String)*)(n: Node) = 
  attrs.map { p =>
    n.attribute(p._1).exists(_ == p._2)
  } reduceLeft(_ && _)

And here's an example that uses it to extract item objects from an atom feed.

def parseAtomItems(ns: NodeSeq) = ns \\ "entry" flatMap { i =>
  for(
    t <- i \ "title";
    l <- i.\("link").filter(attributeValue(
        "type"  -> "text/html",
        "rel"   -> "alternate"
        ).flatMap(_.attribute("href"))
    ) yield FeedItem(t text, l text)
}

My question then: is there a cleaner / more idiomatic way to implement attributeValue?

+2  A: 

I think the code is pretty good, actually. I'd do this:

def attributeValue(attrs: (String, String)*)(n: Node) =   
  attrs forall { 
    case (key, value) => n attribute key exists (_ == value)
  }

Alternatively,

def attributeValue(attrs: (String, String)*)(n: Node) =   
  attrs forall { 
    case (key, value) => n \ ("@"+key) exists (_ == value)
  }

The main advantage to forall over reduceLeft being that the former will stop at the first false result, while the latter will iterate over every key/value pair, even if a false match is guaranteed.

Daniel
Iterable#forall - that's the missing piece. I'm open to other answers, but that is quite nice Daniel, thank you!
Chris