I'm doing a PHP 'traffic light' style warning system for my website, that basically says' if there is X percentage change between the current array entry and the next one, throw an error'.
So, I'm looping through my array elements in a foreach loop, however need to be able to do something like this: (note: this is just a basic example, but should be enough to get the idea)
foreach($array as $a)
{
$thisValue = $a['value'];
$nextValue = next($a['value']);
$percentageDiff = ($nextValue-$thisValue)/$thisValue;
}
I've put next() tags to get the next value but understand this only works for arrays. IS there something else I can use to get the next foreach item?
Thanks for your time!
do it the other way, and store the previous entry and compare those.
$prev = null;
foreach ($item as $i){
if ($prev !== null){
diff($prev, $i);
}
$prev = $i
}
Simple answer: don't use a foreach loop. Use a simple for loop instead:
for ($i = 0; $i < count($array); $i++) {
if (isset($array[$i + 1])) {
$thisValue = $array[$i]['value'];
$nextValue = $array[$i + 1]['value'];
$percentageDiff = ($nextValue-$thisValue)/$thisValue;
}
}
You should be able to use:
foreach($array as $a)
{
$array_copy = $array;
$thisValue = $a['value'];
$nextValue = next($array_copy);
$nextValue = $nextValue['value'];
$percentageDiff = ($nextValue-$thisValue)/$thisValue;
}
This copies the array and then moves the pointer along by 1.
The easiest solution IMO is to change your mindset. Instead of inspecting the current and the next record, inspect the previous and the current record. Remembering the previous one is easier than getting the next one.
If you don't want that, you can also ditch the foreach and iterate C-style using for and a counter variable - one cavet though: PHP's sparse arrays can bite you, so you best call array_values() on the inspected array before iterating.
If you want to work with foreach, you could compare the current value with the previous value instead of with the next value:
$previous = null;
foreach ($array as $a) {
if (!is_null($previous)) {
$thisValue = $previous['value'];
$nextValue = $a['value'];
$percentageDiff = ($nextValue-$thisValue)/$thisValue;
}
$previous = $a;
}
With this you just shift the whole iteration by one item.
Why not just use a normal for loop rather than the foreach iterator?
<?php
$testArray = array(10,20,30,40,50,60,70,80,90,100);
$elementCount = count($testArray);
for($loop=0; $loop<$elementCount; $loop++) {
$thisValue = $testArray[$loop];
// Check if there *is* a next element.
$nextValue = $loop + 1 < $elementCount ? $testArray[$loop + 1] : null;
// Calculate the percentage difference if we have a next value.
if($nextValue) $percentageDiff = ($nextValue-$thisValue)/$thisValue;
}
?>
'It is usually easier to use the previous value than the next:
$lastValue = NULL;
foreach ($array as $a) {
if ($lastValue === NULL) {
$lastValue = $a['value'];
continue;
}
$percentageDiff = ($a['value'] - $lastValue) / $lastValue;
$lastValue = $a['value'];
}
for($i=0;$i<count($array);$i++) {
$thisValue = $array[$i];
$nextValue = $array[i+1]; // will not be set if $i==count($array)-1
$percentageDiff = ($nextValue-$thisValue)/$thisValue;
}
There are indeed array iterator functions which support what you need, and also simply looping thorugh an array with next() / prev() etc works fine but the solution above is more elegant
It doesn't work with foreach because foreach creates a copy of references and doesn't set the array pointers in the array itself.
Here is a sample which can be used for associative arrays using array iterator functions:
$arrayobject = new ArrayObject($array);
$iterator = $arrayobject->getIterator();
for($i=0;$i<count($array);$i++) {
$iterator->seek($i);
$thisValue = $iterator->current();
$iterator->seek($i+1);
$nextValue = $iterator->current();
$percentageDiff = ($nextValue-$thisValue)/$thisValue;
}
Related
can anyone help me with this following Array sorting
Input
$array=array(1,2,3,6,7,8,100,101,200);
Output:
$new_array=array(
0=>array(1,2,3),
1=>array(6,7,8),
2=>array(100,101),
3=>array(200)
);
Thanks in advance!
$array=array(1,2,3,6,7,8,100,101,200);
$new_array = array();
$lastNumber = '';
foreach($array as $number) {
if($lastNumber === '') {
$otherArray = array($number);
}
elseif($lastNumber + 1 !== $number) {
$new_array[] = $otherArray;
$otherArray = array($number);
}
else{
$otherArray[] = $number;
}
$lastNumber = $number;
}
$new_array[] = $otherArray;
print_r($new_array);
You can loop over the array and check the distance to the next element in the array. If this distance is larger then one add a new sub array:
$array=array(1,2,3,6,7,8,100,101,200);
$result=array(array());
for($i=0; $i<count($array)-1; $i++)
{
if($array[$i+1]-$array[$i]==1)
{
// If difference to next number is one -> push
array_push($result[count($result)-1], $array[$i]);
}
else
{
// ... else: Push and create new array for the next element
array_push($result[count($result)-1], $array[$i]);
array_push($result, array());
}
}
// Push the last number
array_push($result[count($result)-1], $array[$i]);
print_r($result);
Just a different approach with array_push()...
Pretty simple: loop through the numbers, remember the last one, if the current number is not the successor of the last one, add a new array to your result, push into the last array in your result.
$result = [];
$last = null;
foreach ($array as $number) {
if ($last !== $number - 1) {
$result[] = [];
}
$result[count($result) - 1][] = $number;
$last = $number;
}
You could even get rid of $last and directly read the last array element of the last array element of $result, but that would make the code actually more complicated.
How can I add to the array that I'm using foreach on?
for instance:
$t =array('item');
$c = 1;
foreach ($t as $item) {
echo '--> '.$item.$c;
if ($c < 10) {
array_push($t,'anotheritem');
}
}
this seems to produce only one value ('item1'). It seems that $t is only being evaluated once (at first time of foreach use) but not after it enters the loop.
foreach() will handle the array you pass into it as a static structure, it can't be dynamic as far as the number of iterations go. You can change the values by passing the value of the iteration by reference (&$value) but you can't add new ones in the same control structure.
for()
for() will let you add new ones, the limit you pass will be evaluated each time, so count($your_array) can be dynamic. Example:
$original = array('one', 'two', 'three');
for($i = 0; $i < count($original); $i++) {
echo $original[$i] . PHP_EOL;
if($i === 2)
$original[] = 'four (another one)';
};
Output:
one
two
three
four (another one)
while()
You can also define your own custom while() loop structure using a while(true){ do } methodology.
Disclaimer: Make sure if you're doing this that you define an upper limit on where your logic should stop. You're essentially taking over the responsibility of making sure that the loop stops somewhere here instead of giving PHP a limit like foreach() does (size of array) or for() where you pass a limit.
$original = array('one', 'two', 'three');
// Define some parameters for this example
$finished = false;
$i = 0;
$start = 1;
$limit = 5;
while(!$finished) {
if(isset($original[$i])) {
// Custom scenario where you'll add new values
if($i > $start && $i <= $start + $limit) {
// ($i-1) is purely for demonstration
$original[] = 'New value' . ($i-1);
}
// Regular loop behavior... output and increment
echo $original[$i++] . PHP_EOL;
} else {
// Stop the loop!
$finished = true;
}
}
See the differences here.
Thanks Scowler for the solution. It was posted in the comments and although he replied with an answer, it wasn't as simple as his first commented suggestion.
$t =array('item');
$c = 1;
for ($x=0; $x<count($t); $x++) {
$item = $t[$x];
echo '--> '.$item.$c;
if ($c < 10) {
array_push($t,'anotheritem');
}
$c++;
}
Works Great! count($t) is re-evaluated each time it goes through the loop.
I'll let the code speak:
$params = array();
$qtyCount = count(array(1,2,3,4,5));
$qtyAr = array(6,7,8,9,10);
$i = 1;
while($i <= $qtyCount){
$params['quantity_'.$i] .= $qtyAr[$i];
$i++;
}
But when I do this, the last value is missing.
BTW: the values in the qtyCount and qtyAr are bugus... just for example.
I would opt for a simpler approach:
array_walk($qtyAr, function($item, $index) use (&$params) {
$key = sprintf("quantity_%u", $index);
$params[$key] = $item;
});
It appears that you are starting at the wrong index (1), $i should be = 0 as others have pointed out.
You're missing the last element because your unasssociated array starts with 0 and your loop starts with 1. This is why foreach works so much better because it iterates over ALL your elements.
$qtyAr = array(6,7,8,9,10);
$i = 1;
foreach($qtyAr as $val) {
$params['quantity_' . $i] = $val;
$i++;
}
I would like to skip a number of iterations of a foreach loop.
I have this code;
$myarray = array("a","b","c","d","e","f","g","h");
$skip = 5;
foreach($myarray as $key => $letter){
if($key < $skip){
$key = $skip;
}
echo $letter;
}
This code doesn't do the trick. But with it I can sort of explain what I want. I what to actually move the pointer of the next iteration. It thought that by modifying the value of the key to the one i want would be enough. I understand that a possible solution would be this.
$myarray = array("a","b","c","d","e","f","g","h");
$skip = 5;
foreach($myarray as $key => $letter){
if($key < $skip){
continue;
}
echo $letter;
}
But that kinda still does the iteration step. I would like to completely jump over the iteration.
Thanks
See: array_slice
$myarray = array("a","b","c","d","e","f","g","h");
foreach(array_slice($myarray, 5) as $key => $letter){
echo $letter;
}
You could just use a for loop instead
EDIT:
for($i = $skip; $skip > 0, $i < count($myarray); $i++) {
// do some stuff
}
That's not really how foreach loops (and iteration in general) work.
You can either do the continue version (which works fine; if it's the first thing in the loop body, it's essentially the same), or you can construct a different array to iterate over that doesn't include the first elements, or you can use a regular for loop.
<?php
$myarray = array("a","b","c","d","e","f","g","h");
$skip = 5;
$index = 0 ;
foreach($myarray as $key => $letter){
if( ($index % $skip) == 0 ){
echo $letter;
}
$index++;
}
?>
$myarray = array("a","b","c","d","e","f","g","h");
foreach (new LimitIterator (new ArrayIterator ($myarray), 5) as $letter)
{
echo $letter;
}
foreach (array_slice($myarray, 5) as $key => $letter) {
[...]
}
You can call $array->next() for $skip times. There are cases when you cannot easily use a regular for loop: for example when iterating a DatePeriod object.
I need to loop and array of objects. In some cases, inside the loop, I need to move the internal pointer to the next element in the array. How do I do that?
foreach($objects as $object)
{
// TODO: move internal pointer to next one?
// next($objects) doesn't work
}
You can't move the array pointer, but you can skip the iteration:
foreach ($objects as $object) {
if (!interesting($object)) {
continue;
}
// business as usual
}
If you need to decide whether to skip the next iteration, you can do something like this:
$skip = false;
foreach ($objects as $object) {
if ($skip) {
$skip = false;
continue;
}
// business as usual
if (/* something or other */) {
$skip = true;
}
}
I'd first check if there isn't any better logic to express what you want though. If there isn't, #netcoder's list each example is the more concise way of doing this.
As previously mentioned, you can use a for loop (only if you have numeric keys though) or continue. Another alternative is to use the list and each iteration method which allows you to move the array pointer with next, prev, etc. (as it does not create a copy of the array like foreach does):
$array = array(1,2,3,4,5);
while (list($key, $value) = each($array)) {
echo $value;
next($array);
}
Will output:
024
Now, that I understand, what you want ;), two other solutions
$c = count($array);
for ($i = 0; $i < $c; $i += 2) {
$item = $array[$i];
}
foreach (range(0, count($array), 2) as $i) {
$item = $array[$i];
}
next($objects)
next — Advance the internal array pointer of an array
Use for loop, like this:
for($i = 0; $i < sizeof($array); $i++)
{
echo $array[$i]->objectParameter;
$i++; //move internal pointer
echo $array[$i]->objectParameter;
$i++; //move internal pointer again
echo $array[$i]->objectParameter;
//$i++; //no need for this because for loop does that
}