At 2010-02-16 11:10 -0500, Tony Mariella wrote:
Is there a way to use fn:distinct-nodes
There is no fn:distinct-nodes in the XPath library ... the functions
and operators specification has an example of what might be one based
on node identity:
http://www.w3.org/TR/2007/REC-xpath-functions-20070123/#func-distinct-nodes-stable
or fn:distinct-values to
return a set of distinct node that are part of the results xml ?
Example:
<results>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal>TEST1</testVal>
</item>
</results>
The highlighted xml is what I would want returned.
Then that isn't the "traditional" implementation of "distinct values"
... you would get one for each of the two distinct ones.
Or if all the items are identical, only return a single item.
A special case.
This is a query result so I would not know the results in advance.
Well, first step, to find the distinct <item> elements would be:
T:\ftemp>type tony.xml
<results>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal>TEST1</testVal>
</item>
</results>
T:\ftemp>xquery tony.xq
<?xml version="1.0" encoding="UTF-8"?>
<results>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal>TEST1</testVal>
</item>
</results>
T:\ftemp>type tony.xq
declare function local:distinct-nodes ($arg as node()*) as node()*
{
for $a at $apos in $arg
let $before_a := fn:subsequence($arg, 1, $apos - 1)
where every $ba in $before_a satisfies not(deep-equal($ba,$a))
return $a
};
<results>
{ local:distinct-nodes( doc('tony.xml')/results/item ) }
</results>
T:\ftemp>
.... but that returns the two distinct <item> elements. It sounds
like you want the only <item> elements that do not have duplicates:
T:\ftemp>type tony.xml
<results>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal>TEST1</testVal>
</item>
</results>
T:\ftemp>xquery tony.xq
<?xml version="1.0" encoding="UTF-8"?>
<results>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal>TEST1</testVal>
</item>
</results>
T:\ftemp>type tony.xq
declare function local:distinct-nodes ($arg as node()*) as node()*
{
for $a at $apos in $arg
where every $other in $arg except $a satisfies not(deep-equal($other,$a))
return $a
};
<results>
{ local:distinct-nodes( doc('tony.xml')/results/item ) }
</results>
T:\ftemp>
... but that won't handle your special case of when they are all
identical you want one returned, that would be added by using:
T:\ftemp>type tony.xml
<results>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
</results>
T:\ftemp>xquery tony.xq
<?xml version="1.0" encoding="UTF-8"?>
<results>
<item>
<addr>24 Short Rd</addr>
<city>Baltimore</city>
<state>MD</state>
<testVal/>
</item>
</results>
T:\ftemp>type tony.xq
declare function local:distinct-nodes ($arg as node()*) as node()*
{
let $result :=
for $a at $apos in $arg
where every $other in $arg except $a satisfies not(deep-equal($other,$a))
return $a
return if( count($result) = 0 ) then $arg[1] else $result
};
<results>
{ local:distinct-nodes( doc('tony.xml')/results/item ) }
</results>
T:\ftemp>
I hope this helps. Though I wonder if you've expressed your
requirement accurately, as it seems incongruous with your use of
terminology. Note also that the execution time could be significant
if you have a lot of items or they are deep items.
. . . . . . . . Ken
p.s. only two seats still remain for the XQuery/XSLT training in
Prague next month
--
XSLT/XQuery/XPath training after http://XMLPrague.cz 2010-03-15/19
XSLT/XQuery/XPath training: San Carlos, California 2010-04-26/30
Vote for your XML training: http://www.CraneSoftwrights.com/q/i/
Crane Softwrights Ltd. http://www.CraneSoftwrights.com/q/
Training tools: Comprehensive interactive XSLT/XPath 1.0/2.0 video
Video lesson: http://www.youtube.com/watch?v=PrNjJCh7Ppg&fmt=18
Video overview: http://www.youtube.com/watch?v=VTiodiij6gE&fmt=18
G. Ken Holman mailto:[email protected]
Male Cancer Awareness Nov'07 http://www.CraneSoftwrights.com/q/bc
Legal business disclaimers: http://www.CraneSoftwrights.com/legal
_______________________________________________
General mailing list
[email protected]
http://xqzone.com/mailman/listinfo/general