Search code examples
phparraysmultidimensional-arrayfilteringcolumnsorting

How to compact/rearrange a multi-dimensional array by removing null values


I have an array called $rows. The first row is the header. The first column is X (input signal), the other columns are Y1,Y2,Y3, etc. (output signals). For any X value some of Y values may be NULL.

I print it with this code:

$first_key = key($rows);
foreach ($rows as $key => $row) {
    if($key == $first_key) {    // print header     
        fputcsv($out, array_keys($row));
    }
    fputcsv($out, $row);
}

The output of this code look like this (sorted by X column):

  X   |  Y1  |  Y2  | Y3 |
--------------------------
 0.1  |      |  10  |    |
 0.5  |  90  |      |  7 |
 0.7  |  15  |  40  |    |
 1.2  |      |  12  |    |

Goal: reorganize output to have columns X1,Y1,X2,Y2, etc such that in every pair (Xi,Yi) no NULL(empty) values are in between the row data and the header:

X1    |  Y1  | X2  | Y2 | X3 | Y3 |
------------------------------------------  
0.5   |  90  | 0.1 | 10 | 0.5| 7 |
0.7   |  15  | 0.7 | 40 |    |   |
      |      | 1.2 | 12 |    |   |

My attempt:

$current_header  = array_keys($rows[0]);
$size = count($current_header);
$new_header=array();   // the new header: X1, Y1, X2, Y2,...
$arr_X=array();
$arr_Y=array();
$x_column=$current_header[0];
for ($i=1; $i<$size; $i++) {
    $y_column=$current_header[$i];
    $new_header[2*$i]   = $x_column;
    $new_header[2*$i+1] = $y_column;
    $arr_Y[$y_column]   = array_column($rows, $y_column);  
    $arr_X[$y_column]   = array_column($rows, $x_column); 
}

Next step: join $arr_X[$y_column] and $arr_Y[$y_column] into arr_XY. I think in this array the key should be the index (row#); also arr_XY should not include the points where $arr_Y[$y_column] is NULL: I do not know how to do it

$arr_XY=array();
for ($i=1; $i<$size; $i++) {
    $y_column=$current_header[$i];
    // here should be the code to join arrays and eliminate NULL arr_Y points 
    $arr_XY[$y_column] = ($arr_X[$y_column], $arr_Y[$y_column]);
} 

The final step is where I need help: build and print the output rows by combining all arr_XY[$y_column] by row index.


Solution

  • Is this what you are after?

    Input:

    $rows=[
        ['X','Y1','Y2','Y3'],
        [.1,null,10,null],
        [.5,90,null,7],
        [.7,15,40,null],
        [1.2,null,12,null]
    ];
    

    Method:

    foreach($rows as $i=>$row){
        if(!isset($result)){                       // prepare result keys in order
            foreach(array_slice($row,1) as $col){  // don't iterate X column of header
                $no=substr($col,1);                // get number following Y
                $result["X$no"]=[];                // declare X column with column integer
                $result["Y$no"]=[];                // declare Y column with column integer
            }
        }else{
            foreach(array_slice($row,1,null,true) as $i=>$col){  // ignore X column
                if(!is_null($col)){                // only store non-null values
                    $result["X$i"][]=$row[0];      // assign X value
                    $result["Y$i"][]=$col;         // assign Y value
                }
            }
        }
    }   
    var_export($result);
    

    Alternative Method:

    foreach($rows as $i=>$row){
        foreach(array_slice($row,1,null,true) as $c=>$col){
            if($i==0){
                $result["X$c"]=[];
                $result["Y$c"]=[];
            }elseif(!is_null($col)){
                $result["X$c"][]=$row[0];
                $result["Y$c"][]=$col;
            }
        }
    }
    

    Output:

    array (
      'X1' => [0.5, 0.7],
      'Y1' => [90, 15],
      'X2' => [0.1, 0.7, 1.2],
      'Y2' => [10, 40, 12],
      'X3' => [0.5],
      'Y3' => [7]
    ];