PHP foreach SplPriorityQueue - can't find the elements - php

I have the following code:
$friendsOrdered= new SplPriorityQueue();
... // Code to fill up the priority queue
print_r($friendsOrdered);
$i = 0;
foreach($friendsOrdered as $f) {
}
echo "<br><br>";
print_r($friendsOrdered);
When I look at the output of the prints I can see that after the loop the priority queue is empty. Is there a way of stopping the foreach from removing elements?
Thanks!

You cannot stop the loop from dequeuing elements, as that is the nature of the data structure. You could, however, create a copy of the object before the loop so your data isn't lost:
$friendsOrdered_copy = clone $friendsOrdered;
foreach($friendsOrdered_copy as $f) {
// ...
}

Related

SPL InfiniteIterator is not working with next or prev

I wanted to over an array infinitely like a circular array.
I have the following setup using InfiniteIterator1, which iterates over the $players infinitely. But actually, I want to know the next player and the previous player from this loop like below
$players = new InfiniteIterator(new ArrayIterator(['Shobi', 'Jomit']));
foreach ($players as $player) {
echo $player; // current player name comes properly infinetly
echo next($players); // next player should come
echo current($players); // current player should come
echo prev($players); //previous player should come
}
but next() and prev() always return null
From the doc, I can see those methods are returning void, but is there any way I can extend the InfiniteIterator and achieve this mechanism required?
How do I make next() and prev() work with InfiniteIterator?
Edit current() returns current item, (meaning, it works properly in the logic)
If instead of using iterators, you can just use an array and a pointer to the 'current' entry and then use a while(true) loop which will just keep going (you can always add a break to stop it for testing or some condition). The various parts of the logic check if the current player is the last one - so the next one is the start one -
or if it's the first item so the previous is the end item. Also the increment resets once it gets to the end and starts over again...
$players = ['Shobi', 'Jomit'];
$playerKey = 0;
$playerCount = count($players);
while(true) {
echo $players[($playerKey+1)%$playerCount].PHP_EOL; // next player
echo $players[$playerKey].PHP_EOL; // current player
echo $players[($playerKey>0)?$playerKey-1:$playerCount-1].PHP_EOL; //previous player
$playerKey = ( $playerKey+1 == $playerCount )?0:$playerKey+1;
}
You can go nuts with PHP iterators and do something like the following:
$array = ['Shobi', 'Jomit', 'John', 'Jane', 'Smith'];
// Use NoRewindIterator to prevent MultipleIterator rewinding.
$players1 = new NoRewindIterator(new InfiniteIterator(new ArrayIterator($array)));
// Go to the end of array, i.e. set prev player.
for ($i = 0, $size = count($array); $i < $size - 1; $i++) {
$players1->next();
}
$players2 = new InfiniteIterator(new ArrayIterator($array));
$players2->next();
// Use NoRewindIterator to prevent MultipleIterator rewinding.
$players3 = new NoRewindIterator(new InfiniteIterator(new ArrayIterator($array)));
$players3->next(); // Go to the second player, i.e. next player
// MultipleIterator will traverse three iterators at once.
// Since the pointer in each iterator differs in one position, we will have prev, curr and next.
$players = new MultipleIterator(MultipleIterator::MIT_NEED_ALL|MultipleIterator::MIT_KEYS_ASSOC);
$players->attachIterator($players1, 'prev');
$players->attachIterator($players2, 'curr');
$players->attachIterator($players3, 'next');
$i = 0;
foreach ($players as $player) {
print_r($player);
if (++$i >= 10) {
break;
}
}
Please, see the demo.
Posting an answer which I finally came up with.
The infinite iterator was not really working for my case. Especially because I was not able to move the pointer back using the prev() function. So I did implement the Iterator Interface and override the next and prev methods accordingly.
So that when next() is called and if it is at the end of the array, it will rewind the pointer to the beginning, likewise, if prev() is called and the pointer is already at the beginning, it will move the pointer to the end of the internal array. This worked for me perfectly.
Relevant code
<?php
class CircularIterator implements Iterator
{
...
...
public function next()
{
next($this->entries);
if (!$this->current()) {
$this->rewind();
}
}
public function prev()
{
prev($this->entries);
if (!$this->current()) {
$this->end();
}
}
....
....
}
full implementation and sample code - Link

How to check if JSON object exists

I made a post regarding a code for Adobe Analytics API. I am using a code to retrieve the first and second element (dimension) which is called 'breakdown'. The number varies depending on the data we have for this element.
I am using a loop with i=10 but I want to take everything. Which is the best way to achieve that?
I am using this code:
foreach ($json->report->data as $element)
{
// putting an excessive number for i
for ($i=0;$i<100;$i++)
{
// checking if the object exists
if (isset($element->breakdown[0]->breakdown[$i]->name))
{
echo $element->breakdown[0]->breakdown[$i]->name;
// putting the data in a list I created before
array_push($list, array(''.$element->year.'-'.$element->month.'-'.$element->day, $element->breakdown[0]->name, $element->breakdown[0]->breakdown[$i]->name, $element->breakdown[0]->breakdown[$i]->counts[0], $element->breakdown[0]->breakdown[$i]->counts[1]));
}
else
{
// Break the loop. All the objects are in the row. So, if there is not any for i=45 then there won't be any for i>45
continue;
}
}
}
where I am trying to get all the objects. I am checking if the objects exist. If they don't, I want this loop to stop (second loop).
Use break instead of continue to exit a loop
break stops the current loop continue goes to the next iteration of the loop.
To get out of both loops use break 2;
You can break the loop with break.
for more information: http://php.net/manual/en/control-structures.break.php
foreach ($json->report->data as $element)
{
// putting an excessive number for i
for ($i=0;$i<100;$i++)
{
// checking if the object exists
if (isset($element->breakdown[0]->breakdown[$i]->name))
{
echo $element->breakdown[0]->breakdown[$i]->name;
// putting the data in a list I created before
array_push($list, array(''.$element->year.'-'.$element->month.'-'.$element->day, $element->breakdown[0]->name, $element->breakdown[0]->breakdown[$i]->name, $element->breakdown[0]->breakdown[$i]->counts[0], $element->breakdown[0]->breakdown[$i]->counts[1]));
}
else
{
// Break the loop.
break;
}
}
}

Adding up the TOTAL number of XML Items with PHP

I am trying to get the total number of bikes available in a bike share system. I am using php and simpleXML to filter the XML data.
I have successfully retrieved the number of bikes at each station.
foreach ($xml->station as $items) {
print $items->nbBikes;
}
But I want the total number of bikes available at all stations. I tried this to declare a variable ($totalBikes) that added to itself each time through the foreach statement, but it did not work.
foreach ($xml->station as $items) {
print $items->nbBikes;
$numberOfBikes = $items->nbBikes;
$totalBikes= $numberOfBikes += $numberOfBikes;
}
print $totalBikes;
Can anyone please suggest a way to get the total number of bikes?
Thanks!
You want to add $numberofbikes to $totalbikes, not to itself:
$totalBikes += $numberOfBikes;
which is a shortcut version of
$totalBikes = $totalbikes + $numberOfBikes;
Be sure you're declaring '$totalbikes' before your foreach loop though so it doesn't get reset on each iteration.
You declared $totalBikes within the loop, so it is getting reset every time you iterate. Also, you're not adding $numberOfBikes to $totalBikes properly. Try this:
$totalBikes = 0;
foreach ($xml->station as $items) {
$numberOfBikes = $items->nbBikes;
$totalBikes += $numberOfBikes;
}
print $totalBikes;
Instead of iterating, use xpath:
$bikes = $xml->xpath("/stations/station/nbBikes");
$sum = array_sum(array_map("intval", $bikes));
line 1 will select all <nbBikes> into an array, but as SimpleXml objects
line 2 will first transform all elements to Integer, then add them up.
Note: For a one-liner, there's a sum-function in xpath, but I couldn't get it to work. Any ideas why?
$sum = $xml->xpath("sum(/stations/station/nbBikes)")[0];

PHP Go to the next element of array

I have created an array list with the following code:
<?php
$ids = array();
if (mysql_num_rows($query1))
{
while ($result = mysql_fetch_assoc($query1))
{
$ids["{$result['user_id']}"] = $result;
}
}
mysql_free_result($query1);
?>
Now, i need to read two elements from the array. The first is the current and the second one is the next element of array. So, the simplified process is the following:
i=0: current_element (pos:0), next_element (pos:1)
i=1: current_element (pos:1), next_element (pos:2)
etc
To do this, i have already written the following code, but i cant get the next element for each loop!
Here is the code:
if (count($ids))
{
foreach ($ids AS $id => $data)
{
$userA=$data['user_id'];
$userB=next($data['user_id']);
}
}
The message i receive is: Warning: next() expects parameter 1 to be array, string given in array.php on line X
Does anyone can help? Maybe i try to do it wrongly.
The current, next, prev, end functions work with the array itself and place a position mark on the array. If you want to use the next function, perhaps this is the code:
if (is_array($ids))
{
while(next($ids) !== FALSE) // make sure you still got a next element
{
prev($ids); // move flag back because invoking 'next()' above moved the flag forward
$userA = current($ids); // store the current element
next($ids); // move flag to next element
$userB = current($ids); // store the current element
echo(' userA='.$userA['user_id']);
echo('; userB='.$userB['user_id']);
echo("<br/>");
}
}
You'll get this text on the screen:
userA=1; userB=2
userA=2; userB=3
userA=3; userB=4
userA=4; userB=5
userA=5; userB=6
userA=6; userB=7
userA=7; userB=8
You get the first item, then loop over the rest and at the end of each loop you move the current item as the next first item ... the code should explain it better:
if (false !== ($userA = current($ids))) {
while (false !== ($userB = next($ids))) {
// do stuff with $userA['user_id'] and $userB['user_id']
$userA = $userB;
}
}
Previous answer
You can chunk the arrays into pairs:
foreach (array_chunk($ids, 2) as $pair) {
$userA = $pair[0]['user_id']
$userB = $pair[1]['user_id']; // may not exist if $ids size is uneven
}
See also: array_chunk()

push into array while it is being looped

Here's a snippet of my code. I understand that it is generally advised against to modify an array whilst it is being looped, however in this scenario I think it is the right thing to do and saves resources.
$levels = array($post);
foreach ($levels as $level) {
$next_sibling = get_next_sibling_page($level);
if ($next_sibling) {
$next_page = $next_sibling;
break 1;
} else if ($level->post_parent) {
array_push($levels, get_page($level->post_parent));
}
}
The idea is that the foreach would reiterate with the pushed value if ($level->post_parent), I can see that the if statement is resolving to true and the array is being pushed to however the foreach does not reiterate and only runs the one time.
Does anyone know how I can recursively continue my foreach until $level->post_parent resolves to false?
To modify array which is being used in foreach pass array by referance.
foreach (&$levels as $level) {
.....
}

Categories