views:

271

answers:

3

I have an array:

Array
(
    [1] => 25
    [2] => 50
    [3] => 25
)

I would like to make it into:

Array
(
    [1] => 50
    [2] => 50
)

To do this I split the middle value between 1 and 3. This is the simplest example, where the split is 50,50. I would like to be able to take a 15 element array down to 6 elements.

Any ideas?

Additional Examples [10, 15, 20, 25] Reduced to two elements: 25(10 + 15),45(20 + 25) [10, 10, 10, 10, 11] Reduced to two elements: 25(10 + 10 + (10/2)),26((10/2) + 10 + 11)

A: 

You could sum the values using array_sum() and then, depending on the number of elements you want to have in your resulting array, divide that sum and fill every element you want to keep with the result of your division.

(Here I'm assuming you'll use a second array, but you could unset the unneeded if you prefer it that way).

Nicolas
I would like to keep the distributed value percents the same. If I used the data to produce a bar graph, I need the reduce the data, but keep the overall shape of the graph.
Davin
A: 

Here's my stab at your issue

<pre>
<?php

class Thingy
{
  protected $store;
  protected $universe;

  public function __construct( array $data )
  {
    $this->store = $data;
    $this->universe = array_sum( $data );
  }

  public function reduceTo( $size )
  {
    //  Guard condition incase reduction size is too big
    $storeSize = count( $this->store );
    if ( $size >= $storeSize )
    {
      return $this->store;
    }

    //  Odd number of elements must be handled differently
    if ( $storeSize & 1 )
    {
      $chunked = array_chunk( $this->store, ceil( $storeSize / 2 ) );
      $middleValue = array_pop( $chunked[0] );

      $chunked = array_chunk( array_merge( $chunked[0], $chunked[1] ), floor( $storeSize / $size ) );

      //  Distribute odd-man-out amonst other values
      foreach ( $chunked as &$chunk )
      {
        $chunk[] = $middleValue / $size;
      }
    } else {
      $chunked = array_chunk( $this->store, floor( $storeSize / $size ) );
    }

    return array_map( 'array_sum', $chunked );
  }

}

$tests = array(
    array( 2, array( 25, 50, 25 ) )
  , array( 2, array( 10, 15, 20, 25 ) )
  , array( 2, array( 10, 10, 10, 10, 11 ) )
  , array( 6, array_fill( 0, 15, 1 ) )
);

foreach( $tests as $test )
{
  $t = new Thingy( $test[1] );
  print_r( $t->reduceTo( $test[0] ) );
}

?>
</pre>
Peter Bailey
The last example does not produce what I need, but I will work out this by doubling the data points until I reach more or equal to needed count. Thank you very much!
Davin
A: 

After doing additional tests on Peter's solution, I noticed it did not get me what I expected if the reduce to size is an odd number. Here is the function I came up with. It also inflates data sets that are smaller then the requested size.

   <?php
        function reduceto($data,$r) {
            $c = count($data);

      // just enough data
      if ($c == $r) return $data;

            // not enough data
            if ($r > $c) {
             $x = ceil($r/$c);
             $temp = array();
             foreach ($data as $v) for($i = 0; $i < $x; $i++) $temp[] = $v;
             $data = $temp;
             $c = count($data);
            }

            // more data then needed
            if ($c > $r) {
             $temp = array();
             foreach ($data as $v) for($i = 0; $i < $r; $i++) $temp[] = $v;
             $data = array_map('array_sum',array_chunk($temp,$c));
            }
            foreach ($data as $k => $v) $data[$k] = $v / $r;
            return $data;
        }
    ?>
Davin