5

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!

Jonas
  • 121,568
  • 97
  • 310
  • 388
Sk446
  • 1,240
  • 3
  • 19
  • 38
  • possible duplicate of [Peek ahead when iterating an array in PHP 5.2](http://stackoverflow.com/questions/2458099/peek-ahead-when-iterating-an-array-in-php-5-2) – Gordon Jan 21 '11 at 15:34
  • Thanks for the responses - all seemed like really great ways of getting this done. I opted for the solution provided by @Orbling and @contagious - Whilst it does cause a very moddest overhead, I can live with that given how often the function will be called. – Sk446 Jan 21 '11 at 16:29

8 Answers8

10

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
}
helloandre
  • 10,541
  • 8
  • 47
  • 64
4

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;
    }
}
lonesomeday
  • 233,373
  • 50
  • 316
  • 318
1

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.

Matt Lowden
  • 2,586
  • 17
  • 19
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.

tdammers
  • 20,353
  • 1
  • 39
  • 56
1

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.

Gumbo
  • 643,351
  • 109
  • 780
  • 844
1

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;
    }
?>
John Parker
  • 54,048
  • 11
  • 129
  • 129
  • 1
    This doesn't work for associative arrays like dictionaries and hashtables, right? – Brandon Horsley Jan 21 '11 at 15:34
  • @Brandon You'd have to extract the array_keys and iterate using those in that instance. – John Parker Jan 21 '11 at 15:35
  • @Brandon Incidentally, I was only outlining a potential route, this (and indeed all the others to date) obviously aren't usable implementations as they happily stomp all over the 'percentage diff' each time they iterate. :-) – John Parker Jan 21 '11 at 15:41
  • 1
    @middparka I figured you understood this, the comment was more for the original poster. It may not be as obvious to others. – Brandon Horsley Jan 21 '11 at 16:03
  • @Brandon Thought so. However, the answer by @contagious that uses the foreach iterator is considerably more elegant, so I doubt any eyes will fall this low down. Nice to elucidate such things anyway, that said. :-) – John Parker Jan 21 '11 at 16:08
1

'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'];
}
Orbling
  • 20,413
  • 3
  • 53
  • 64
  • Previous value would work - that'll make it much easier actually...not sure why I didn't think of that :) – Sk446 Jan 21 '11 at 16:24
1
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;
        }
Antony D'Andrea
  • 991
  • 1
  • 16
  • 35
The Surrican
  • 29,118
  • 24
  • 122
  • 168
  • This doesn't work for associative arrays like dictionaries and hashtables, right? – Brandon Horsley Jan 21 '11 at 15:36
  • thats correct, thats why i linked the array iterator functions which are perfectly suited. another possibility is to remember the last array element and calculate based on that and shift the output by one entry - i added a sample code to the answer – The Surrican Jan 21 '11 at 17:49