Say I have this data:
<recipe name="Fruit Salad">
<ingredient name="banana"/>
<ingredient name="oRange"/>
<ingredient name="APPLE"/>
</recipes>
and I have this other data:
<refrigerator>
<item name="appLe"/>
<item name="Grape"/>
</refrigerator>
and I want to find all the things needed for my recipes that are not already in my frig, to make a shopping list. The right answer is the set {banana, orange}
This doesn't work because lower-case()
only takes a string. So does translate()
:
<xsl:variable name="missing-ingredients" select="recipe/ingredient/@name[not(lower-case(.)=lower-case(refrigerator/item/@name))]"/>
Without resorting to cheating (going outside xslt
and xpath
), how can I do this? I find it hard to imagine they didn't make a provision for this inside xpath
, since node lookups are the bread and butter of xslt
and xpath
, and weren't not on the first versions anymore.
If I could just convert a list of nodes (a node-set, I suppose) to lowercase, I would be home free.
There's a problem with this XPath expression:
recipe/ingredient/@name[not(lower-case(.)=lower-case(refrigerator/item/@name))]
The expresson refrigerator/item/@name
yields a sequence of attributes, rather than a single string, as the lower-case()
function expects. You need to lower-case each one of the items in that sequence, by passing them to the lower-case()
function individually. e.g.
for
$stored-ingredient
in
refrigerator/item/@name
return
lower-case($stored-ingredient)
In XPath 3 there's a more concise alternative, the "simple map" operator !
, which would look like this:
refrigerator/item/@name ! lower-case(.)