How to reset the keys in a basic numeric array

I have to compare elements in an array, apart from the first and last they are compared to their immediate neighbours.

If they fail the test, I have to remove the chosen item completely from the array, then start the comparison running again, minus the missing one.

I can get rid of an element with unset, but how to I reset the array again, so that all the values stay in order, but I make up the missing key?

Heres a contrived but simple example, lets say that if the difference between the preceding and following values is more than 19, it must be removed.


$test = array(
0=>111, // ok, its the first one
1=>110, // ok
2=>120, // bad!
3=>130, // bad!
4=>150, // ok, its the last one
);

The first iteration runs and key #1 is good, the diff between 111 and 120 is only 9.

The second iteration runs and key #2 is singled out as being bad, and unset()

Which leaves me:



$test = array(
0=>111, // ok
1=>110, // ok
3=>130, // bad!
4=>150, // ok
);

I was working on the principle of using a $ctr and just assessing 3 items at a time, but I need to reset the array at this point in order to make my assessment again - or do I?

At the moment I am trying to work out how to get this after the first iteration:



$test = array(
0=>111, // ok
1=>110, // ok
2=>130, // bad!
3=>150, // ok
);

I thought there was a sort function which drops key values, but cannot find one.

I can think of ways round this, but just thought I’d ask here.

My first thought would be array_values().

Sorry Paul, I skim read.


<?php
error_reporting(-1);
ini_set('display_errors', true);

function unset_n_reset(&$arr, $key){
  unset($arr[$key]);
  $arr = array_merge($arr);
}

$arr = array(
  0=>111, // ok, its the first one
  1=>110, // ok
  2=>120, // bad!
  3=>130, // bad!
  4=>150, // ok, its the last one
);

unset_n_reset($arr, 3);

print_r($arr);

/*
  Array
  (
      [0] => 111
      [1] => 110
      [2] => 120
      [3] => 150
  )
*/

Nice one SJH, that seems to be the boy, just in time to stop me entering the realms of nuttiness by running array_reverse() twice :expressionless:

unset_n_reset(), yeah looked in the manual for that first, couldn’t find it. I knew the & would be fitting into the solution somewhere.

Nice one chaps.

Is ArrayObject an option? Hide implementation and all that.


<?php
error_reporting(-1);
ini_set('display_errors', true);

$arr = array(
  0=>111, // ok, its the first one
  1=>110, // ok
  2=>120, // bad!
  3=>130, // bad!
  4=>150, // ok, its the last one
);

class ResettingArray extends ArrayObject
{
  public function offsetUnset($index){
    parent::offsetUnset($index);
    parent::__construct(
      array_merge(parent::getArrayCopy())
    );
  }
}

$obj = new ResettingArray($arr);

unset($obj[3]);

print_r(
  (array)$obj
);

/*
  Array
  (
      [0] => 111
      [1] => 110
      [2] => 120
      [3] => 150
  )
*/

Right, seeing as you seem to be in the mood.

I was working my way through this problem and thought I’d pop it up here when done - just to your take on how it ought to look in ArrayObject land, I’m almost done just not got it iterating properly - so, here goes…

Here’s the problem.

A polygon has been drawn, a square, the author has added 2 extra nodes, which are simply not needed.



$extra_node = array( // TLH corner of a square, with two extra straight-line nodes
array('x' => 1, 'y' =>4 ) ,
array('x' => 4, 'y' =>4 ) ,
array('x' => 4, 'y' =>1 ) ,
array('x' => 3, 'y' =>1 ) ,
array('x' => 2, 'y' =>1 ) ,
array('x' => 1, 'y' =>1 ) ,
);


Cunningly I can identify a straight line using this algo:



function assertStraightLine( $first, $centre, $last ){

// takes 3 points and checks if it is straight

$top_slope = (($centre['x']-$first['x'])*($centre['y']-$last['y'])) ;
$btm_slope = (($centre['y']-$first['y'])*($centre['x']-$last['x'])) ;

// chuck some feedback out for now
echo '<h3>' . __FUNCTION__ .' says: </h3>' ;
echo "<hr />Top Slope: $top_slope <hr /> Btm Slope: $btm_slope <hr /> " ;

$diff = ($top_slope - $btm_slope) ;

echo "Diff = $diff <hr /> " ;

    if ( $diff === 0 )
        return true ;


    return false ;
}


So, I feed it the 3 arrays of points, my target and those before and after it.



function unset_n_reset(&$arr, $key){
  unset($arr[$key]);
  $arr = array_merge($arr);
}



$size = count( $extra_node ) ;
$ctr = 1 ;
$input = $extra_node ;

while( $size > $ctr+1 ){

echo "<br />Analyze key number $ctr <br /> " ;

$current = $ctr ;
if( assertStraightLine($input[$current-1], $input[$current], $input[$current+1]) ){

echo 'now remove the node<br /> ';
unset_n_reset($input, $current);
echo 'now reset the node<br /> ';
echo 'now reset the ctr<br /> '; 

}

$ctr++ ;
}

var_dump( $input );

I was just going to switch the while to a foreach and iron out some of the errors, but if you can see how it can be improved by using ArrayObject from the get go, so much the better.

Oh, if there is anyone else out there with little/no algebraic background (like me) this is what the plot looks like


A +++++++++ B
+          +
+          +
+          +
+          +
+          +
F ++E++D+++ C

X direction -> 

The ideas is to get rid of point D and E because there are surplus to requirement, they lie on a straight line.

I don’t see how we can iterate (with stability) through the array if we’re changing it as we go.

Just so I’ve got it clear in my head, I’ve got this to work with.


<?php
error_reporting(-1);
ini_set('display_errors', true);

$plots = array(
  array('x' => 1, 'y' => 4),
  array('x' => 4, 'y' => 4),
  array('x' => 4, 'y' => 1),
  array('x' => 3, 'y' => 1),
  array('x' => 2, 'y' => 1),
  array('x' => 1, 'y' => 1),
);

$opt = array();

foreach($plots as $index => $plot){

  $hasPrev = array_key_exists($index - 1, $plots);
  $hasNext = array_key_exists($index + 1, $plots);

  if( ! $hasPrev || ! $hasNext){
    array_push($opt, $plot);
    continue;
  }

  $prev = $plots[$index - 1];
  $next = $plots[$index + 1];

  $top = ($plot['x']-$prev['x']) * ($plot['y']-$next['y']);
  $btm = ($plot['y']-$prev['y']) * ($plot['x']-$next['x']);

  if(0 === $top - $btm){
    continue;
  }

  array_push($opt, $plot);
}

print_r(
  $opt
);

/*
  Array
  (
      [0] => Array
          (
              [x] => 1
              [y] => 4
          )
  
      [1] => Array
          (
              [x] => 4
              [y] => 4
          )
  
      [2] => Array
          (
              [x] => 4
              [y] => 1
          )
  
      [3] => Array
          (
              [x] => 1
              [y] => 1
          )
  )
*/

OK, getting there.


<?php
error_reporting(-1);
ini_set('display_errors', true);

class Filter extends FilterIterator
{
  public function accept(){
    $hasPrev = $this->getInnerIterator()->offsetExists($this->key() - 1);
    $hasNext = $this->getInnerIterator()->offsetExists($this->key() + 1);

    if( ! $hasPrev || ! $hasNext){
      return true;
    }

    $prev = $this->getInnerIterator()->offsetGet($this->key() - 1);
    $plot = $this->current();
    $next = $this->getInnerIterator()->offsetGet($this->key() + 1);

    $top = ($plot['x']-$prev['x']) * ($plot['y']-$next['y']);
    $btm = ($plot['y']-$prev['y']) * ($plot['x']-$next['x']);

    return 0 !== $top - $btm;
  }
}

$plots = array(
  array('x' => 1, 'y' => 4),
  array('x' => 4, 'y' => 4),
  array('x' => 4, 'y' => 1),
  array('x' => 3, 'y' => 1),
  array('x' => 2, 'y' => 1),
  array('x' => 1, 'y' => 1),
);

$opt = array();

foreach(new Filter(new ArrayIterator($plots)) as $plot){
  #ideally you'd do stuff here
  array_push($opt, $plot);
}

print_r(
  $opt
);

/*
  Array
  (
      [0] => Array
          (
              [x] => 1
              [y] => 4
          )

      [1] => Array
          (
              [x] => 4
              [y] => 4
          )

      [2] => Array
          (
              [x] => 4
              [y] => 1
          )

      [3] => Array
          (
              [x] => 1
              [y] => 1
          )
  )
*/

This looks a little cleaner too.


$plots = array(
  array('x' => 1, 'y' => 4),
  array('x' => 4, 'y' => 4),
  array('x' => 4, 'y' => 1),
  array('x' => 3, 'y' => 1),
  array('x' => 2, 'y' => 1),
  array('x' => 1, 'y' => 1),
);

$plots = iterator_to_array(
  new Filter(new ArrayIterator($plots)),
  false
);

Hats off Anthony, no honestly, silly Christmas hats off now…

Thats very nice, thanks!