Beginner programmer here.
I am in php, working with a dom document nodelist comprising of selected nodes retrieved from an XPath query on an xml file. Excerpt of contents of nodelist:
<properties>
<property>
<price>270</price>
<address>3/2 Farran Street, Castlemaine, VIC, Australia</address>
<infants>3</infants>
</property>
<property>
<price>250</price>
<address>2/37 William Street, Castlemaine, VIC, Australia</address>
<infants>2</infants>
</property>
<property>
<price>250</price>
<address>2/37 William Street, Castlemaine, VIC, Australia</address>
<infants>3</infants>
</property>
...
I wish to sort the nodelist by price
ascending, and in the case of matching prices, by no of infants
descending. In the above snippet, for example, the result would be:
<property>
<price>250</price>
<address>2/37 William Street, Castlemaine, VIC, Australia</address>
<infants>3</infants>
</property>
<property>
<price>250</price>
<address>2/37 William Street, Castlemaine, VIC, Australia</address>
<infants>2</infants>
</property>
<property>
<price>270</price>
<address>3/2 Farran Street, Castlemaine, VIC, Australia</address>
<infants>3</infants>
</property>
I have seen some sorting questions answered relating to multi-dimensional arrays but I can't seem to translate these to nodelists. I can sort by price
ok (see below), but I can't determine how to then sort by infants
.
$properties = iterator_to_array($matches); // where matches = a return from an XPath query
usort($properties, 'sortByPrice');
function sortByPrice ($a, $b) {
$aPrice = $a->getElementsByTagName("price");
$aPrice = $aPrice->item(0)->nodeValue;
$bPrice = $b->getElementsByTagName("price");
$bPrice = $bPrice->item(0)->nodeValue;
return (int) $aPrice - (int) $bPrice;
}
if your code works ok for price, why not just extend it:
$properties = iterator_to_array($matches); // where matches = a return from an XPath query
usort($properties, 'sortByPrice');
function sortByPrice ($a, $b) {
$aPrice = $a->getElementsByTagName("price");
$aPrice = $aPrice->item(0)->nodeValue;
$bPrice = $b->getElementsByTagName("price");
$bPrice = $bPrice->item(0)->nodeValue;
$r = (int) $aPrice - (int) $bPrice;
if (!$r) {
$ainfants = $a->getElementsByTagName("infants");
$ainfants = $ainfants->item(0)->nodeValue;
$binfants = $b->getElementsByTagName("infants");
$binfants = $bPrice->item(0)->nodeValue;
$r = (int) $ainfants - (int) $binfants;
}
return $r;
}
Try this function. It acts as a generalisation of the uasort
PHP function:
function sortByKeyValue($the_array, $key)
{
$cmp_val="((\$a['$key']>\$b['$key'])?1:
((\$a['$key']==\$b['$key'])?0:-1))";
$cmp=create_function('$a, $b', "return $cmp_val;");
uasort($the_array, $cmp);
return $the_array;
}
In your case, you may use it thus:
$properties = iterator_to_array($matches);
sortByKeyValue($properties, 'price');
sortByKeyValue($properties, 'infants');
I hope it helps.
You can fetch scalar values depending on a context node with DOMXPath::evaluate()
. number()
will cast the first node in the location path result into a float. If no node was found the result is zero.
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$properties = iterator_to_array($xpath->evaluate('//properties/property'));
usort(
$properties,
function(DOMElement $a, DOMElement $b) use ($xpath) {
$aPrice = (int)$xpath->evaluate('number(price)', $a);
$bPrice = (int)$xpath->evaluate('number(price)', $b);
if ($aPrice == $bPrice) {
$aInfants = (int)$xpath->evaluate('number(infants)', $a);
$bInfants = (int)$xpath->evaluate('number(infants)', $b);
return $bInfants - $aInfants;
}
return $aPrice - $bPrice;
}
);