0

I have an xml sheet like:

<f:device-list>
    <f:device name="Voltage Converter" quantity="2" serial_number="20011">
        <f:weight units="pounds">3.00</f:weight>
    </f:device>
    <f:device name="24-port switch" quantity="2" serial_number="24PORTSW-004">
        <f:weight units="pounds">3.34</f:weight>
    </f:device>
</f:device-list>

I am obtaining the weight of the devices using simplexml and xpath like so:

foreach($xml->xpath('//f:weight[@units="pounds"]') as $weightLB) {
        echo $weightLB;
        echo "</br>";
}

I am trying to figure out how I can obtain the min and max values of the weight element. I have looked at many solutions regarding xpath but most cover attribute values and are not applicable to a sheet formatted like this. Any help would be appreciated!

Slothie
  • 3
  • 2
  • Why not use `max($weightLB)` and `min($weightLB)`? – Felippe Duarte Sep 17 '19 at 18:45
  • It is a simple xml object and throws the error "When only one parameter is given, it must be an array" – Slothie Sep 17 '19 at 18:47
  • If you want a pure XPath version - have a read of https://stackoverflow.com/a/1129929/1213708. For this case Max - `//f:weight[@units="pounds"][. > //f:weight[@units="pounds"]]` – Nigel Ren Sep 17 '19 at 19:07
  • @NigelRen that answer has some problems. Check my solution below https://stackoverflow.com/a/57981655/11102282 – Alejandro Sep 17 '19 at 20:30

2 Answers2

0

You can map all the values to an array then use max and min functions:

<?php

$xml = <<<XML
<?xml version='1.0'?>
    <device-list>
    <device name="Voltage Converter" quantity="2" serial_number="20011">
        <weight units="pounds">3.00</weight>
    </device>
    <device name="24-port switch" quantity="2" serial_number="24PORTSW-004">
        <weight units="pounds">3.34</weight>
    </device>
</device-list>
XML;

$simpleXml = simplexml_load_string($xml);

$weights = array_map(function($elem) {
    return $elem->__toString();
}, $simpleXml->xpath('//weight[@units="pounds"]'));

echo "Min: " .min($weights) . "<br/>";
echo "Max: " .max($weights);

Output:

Min: 3.00

Max: 3.34

Community
  • 1
  • 1
Felippe Duarte
  • 14,901
  • 2
  • 25
  • 29
0

I'm adding this pure XPath 1.0 answer because the linked one in comments is wrong for this case. The general idiom for maximum:

($node-set[not(. < $node-set)])[1]

Meanning:

Select the first (in document order) from the node set with number value not less than any other in the node set.

For your input (now wellformed):

<f:device-list xmlns:f="dummy">  
  <f:device name="Voltage Converter" quantity="2" serial_number="20011"> 
    <f:weight units="pounds">3.00</f:weight> 
  </f:device>  
  <f:device name="24-port switch" quantity="2" serial_number="24PORTSW-004"> 
    <f:weight units="pounds">3.34</f:weight> 
  </f:device> 
</f:device-list>

This XPath 1.0 expression (with f prefix bound to dummy namespace URI):

(//f:weight[@units='pounds'][not(. < //f:weight[@units='pounds'])])[1]

It selects:

<f:weight xmlns:f="dummy" units="pounds">3.34</f:weight>

Check in http://www.xpathtester.com/xpath/1969ef047d90f2efbb0dcc1d1e131428

Do note: positional predicates (like [1] abbreviated form) work against location step axis, meaning that if all the nodes in the set are first child then you would need parentheses to get the first in document order.

Alejandro
  • 1,882
  • 6
  • 13
  • Sorry - I'm not sure where you are saying how the linked answer didn't work for this case. You seems to swaped < for not >. – Nigel Ren Sep 17 '19 at 20:48
  • @NigelRen There is also a proper explanation of positional predicates. Check the linked answer against OP input sample with duplicated maximum [here](http://www.xpathtester.com/xpath/2c6ca13a1cd0c6659350c577ad0b3c5a) – Alejandro Sep 17 '19 at 20:52