I need a bit of help ordering a SimpleXML array in an alphabetical order using one of it's attribute ($url). I have the following code that generate a page:
<?php
$xml = simplexml_load_file($xml_url);
$ids = array();
foreach($xml->list as $id) {
$ids[] = $id;
}
//It works but randomize instead of sorting alphabetically
usort($ids, function ($a, $b) {
return strnatcmp($a['url'], $b['url']);
});
foreach ($ids as $id) {
PopulatePage($id);
}
function PopulatePage($id) {
$url = $id->url;
$img = $id->img;
//OTHER CODES TO GENERATE THE PAGE
}?>
QUESTION RESOLVED!
There is no conversion needed, you already have an array which you can sort, you just need to understand how usort callbacks work. Each of the items in your $ids array is a SimpleXMLElement object, and each time your callback is run, it will be given two of those objects to compare. Those objects will be exactly the same as in the existing PopulatePage function, so accessing the URL needs to happen exactly as it does there ($url = $id->url;) not using a different notation ($id['url']).
To make it more explicit, let's write a named function with lots of clear variable names:
function CompareTwoIds(SimpleXMLElement $left_id, SimpleXMLElement $right_id): int {
$left_url = $left_id->url;
$right_url = $right_id->url;
return strncatcmp($left_url, $right_url);
}
Now you can test calling that function manually, and use it as the callback when you're happy:
usort($ids, 'CompareTwoIds');
Once you're comfortable with the principles, you may decide to skip the extra verbosity and just write this, which is completely equivalent:
usort($ids, fn($a,$b) => strncatcmp($a->url, $b->url));
Related
I want to sort an array of strings where the substring I want to use as sorting identifier is in the middle of the string in question. I've already tried to do that using a very crude technique and I was wondering if there was a better way to do this.
Here is what I've come up with so far:
<?php
$tracks = array(
"Arcane Library_Track2.mp3",
"Deeper Ocean_Track4.mp3"
"Neon Illumination_Track3.mp3",
"Sound Of Cars_Track1.mp3",
);
$tracksreversed = array();
$sortedtracks = array();
foreach( $tracks as $t) {
array_push( $tracksreversed, strrev($t) );
}
sort($tracksreversed);
foreach( $tracksreversed as $t ) {
array_push( $sortedtracks, strrev($t) );
}
$tracks = $sortedtracks;
?>
This method works but there are two issues. For one it uses two loops and the second problem is that this method will fail as soon as we include more file types like ogg or wav.
Is there a way to easily sort this string according to the _Track1, _Track2, _Track3 indentifiers? Maybe some regex sorting could be done here or perhaps there is some other method I could do which makes this more compact?
Maybe the usort function will be useful. Like:
$getIdx = function(string $name) {
preg_match('/_Track(?<idx>\d+)/', $name, $matches);
return $matches['idx'];
};
usort($tracks, function ($a, $b) use ($getIdx) {
return $getIdx($a) <=> $getIdx($b);
});
Seems to work https://3v4l.org/j65rd
I have the following JSON data
{
"items":[
{"country":"AE","counts":145,"seen":785 },
{"country":"AR","counts":786,"seen":442},
{"country":"AT","counts":132,"seen":743}
]
}
I need to sort array by counts or seen within foreach loop like numbers descending
Here's my code
$jsonobj = json_decode($json, true);
//////////////////////////
foreach($jsonobj['items'] as $items){
usort($jsonobj, "custom_sort");
function custom_sort($a,$b) {
return $a['counts']>$b['counts'];
}
$country = $items['country'];
$counts = $items['counts'];
$seen = $items['seen'];
}
Any help would be appreciated thanks
There are a number of issues with your attempt:
it produces an error because the custom_sort function is declared after the usort function which tries to use it. Move it beforehand (and out of the loop, it makes no sense to keep repeating it). You should have noticed this when trying to run it, although you didn't mention it in your question.
You don't need a loop anyway - usort will work on the whole array at once
You're passing the wrong thing to usort - it needs to work on the "items" property within your object, that's where the array is.
Your comparison function is backwards - it needs to check less than, rather than greater than, if you want to sort in descending order
Returning booleans from the custom sort function is deprecated - you get a warning which tells you to return an integer instead.
This code fixes all of the problems I've listed:
$json = '{"items":[{"country":"AE","counts":145,"seen":785},{"country":"AR","counts":786,"seen":442},{"country":"AT","counts":132,"seen":743}]}';
$jsonobj = json_decode($json, true);
function custom_sort($a,$b) {
return $a['counts'] < $b['counts'] ? 1 : 0;
}
usort($jsonobj["items"], "custom_sort");
var_dump($jsonobj);
Live demo: http://sandbox.onlinephpfunctions.com/code/dff4ed04f7c332704438fc5225f8de936a389221
I'm wondering when, if ever it would be appropriate to use an ArrayObject() instead of an Array()? Here's an example i've been working on.
To me i'm thinking a simple array will work, but i found ArrayObject() in the manual and I'm wondering, if it would be better to use one instead of a simple array.
public function calculateTotal(){
if(count($this->items) > 0){
$n = 0;
foreach($this->items as $item){
if($item->size == 'small'){
$k = $item->price->small;
}
if($item->size == 'large'){
$k = $item->price->large;
}
$n += $k * $item->quantity;
}
}else{
$n = 0;
}
return (int) $n;
}
Now I'm confused as to how i should construct the object.
for instance can i construct it with the short array syntax?
$this->items = []; //empty object
or should i construct is as an Array object
$this->items = new ArrayObject(); //empty object
I'm also confused as to how exactly i should push new items to the array.
i have the following function i'm writing:
Also how should i append arrray objects to this object?
is this good?
public function additem($item){
$add = [
'item_id'=>$this->item_id(),
'name'=>$item['name'],
'size',$item['size'],
'quantity'=>$item['quantity'],
'price'=>[
'large'=>$item['price'],
'small'=>$item['price']
]
]
array_push($this->items,$add);
}
or should i instead use ArrayObject::append() or some other method?
I checked the manual and it says this:
public void ArrayObject::append ( mixed $value )
Appends a new value as the last element.
Note:
This method cannot be called when the ArrayObject was constructed from an object. Use ArrayObject::offsetSet() instead.
source http://php.net/manual/en/arrayobject.append.php
The reason i'm asking this now is, later on when needing to delete items from this list how will i find what i'm looking for? can i use in_array() on this object.
I apologize in advance for these questions that might seem dumb to you, but keep in mind I'm still learning some of the more technical things. Thank you
There's nothing in your first snippet that would require ArrayObject. KISS and use simple arrays: array_push($this->items,$add); or $this->items []= $add; are both fine.
As a side note, there's a discrepancy between calculateTotal and add in your code: you have to decide whether you want your item structures to be arrays ($item['price']) or objects ($item->price). My advice is to use arrays, but that's really up to you.
I have a folder of XML files that look like this, all with different timestamps
<?xml version="1.0"?>
<comment>
<timestamp>1390601221</timestamp>
</comment>
I'm using the glob function to put all of these into an array
$xmls = glob("xml/*.xml");
Then I'm trying to put the timestamp value and xml path into a new array so I can sort by the timestamp. This is how I'm doing it.
$sorted_xmls = array();
foreach ($xmls as $xml) {
$raw_xml = file_get_contents($xml);
$data = simplexml_load_string($raw_xml);
$time = $data->timestamp;
array_push($sorted_xmls, array($time, $xml));
}
All of this seems to work fine. Now I want to sort by timestamp. With the newest first.
foreach ($sorted_xmls as $key => $row) {
$final_sorted[$key] = $row[0];
}
array_multisort($final_sorted, SORT_ASC);
It doesn't seem to be working as expected. Am I doing something wrong? I assume it's on the sorting portion
You are calling array_multisort() in the wrong way here. The way you need to call it is as in example #3 on the manual page, "sorting database results".
The way this works is that you pass the "columns" you want to sort by, and the flags to sort that column by, in order, then pass the target array (the array that will actually be sorted) as the last argument.
So if you change your last line to this:
array_multisort($final_sorted, SORT_ASC, $sorted_xmls);
...then $sorted_xmls should be sorted in the way you would like it to be.
However, a more efficient, albeit more complex, way to do this might be to sort the $xmls array directly using usort(), and load the files from disk at the same time.
$xmls = glob("xml/*.xml");
usort($xmls, function($a, $b) {
// Temporary array to hold the loaded timestamps
// Because this is declared static in a closure, it will be free'd when
// the closure goes out of scope, i.e. when usort() returns
// If you want to store the timestamps for use later, you can import a
// reference to an external variable into the closure with a use() element
static $timestamps = array();
// Load XML from disk if not already loaded
if (!isset($timestamps[$a])) {
$timestamps[$a] = simplexml_load_file($a)->timestamp;
}
if (!isset($timestamps[$b])) {
$timestamps[$b] = simplexml_load_file($b)->timestamp;
}
// Return values appropriate for sorting
if ($timestamps[$a] == $timestamps[$b]) {
return 0;
}
return $timestamps[$a] < $timestamps[$b] ? 1 : -1;
});
print_r($xmls);
I got a 2d-array containing a "column" on which this whole array is to be sorted. As I learned here, it is quite straightforward using array_multisort. My problem is, that this to be sorted over column contains values that needs to be compared in an unusual way. So I thought of something like this:
function main(){
$toBeSorted = array(array(...), ..., array(...));
$sortColumnIndex = n;
$sort_column = array();
//Code copied from provided link
foreach ($toBeSorted as $row)
$sort_column []= $row[$sortColumnIndex];
array_multisort($this->comparator(),$sort_column, $toBeSorted);
}
function comparator(a,b){
return 1;
}
As you can see, I want to pass my comparator to that sort-function. I probably think to much in a non-php way.
There is the usort function, which sorts by using a callback.
Otherwise you could have a look at the array_walk and array_walk_recursive functions, which iterate over an array and apply a function to every member.
I solved it by transforming my sorting space in one, that array_multisort can handle:
...
$sortColumn = array();
foreach($toBeSorted as $value){
$sortColumn[] = $this->transform($value);
}
array_multisort($sortColumn, $toBeSorted);
...
my transformation function simply does everything I imagined the callback would do.
function transform($value){
//i.e. return real part of complex number
//or parse strings or any kind of strange datatypes
}