Home > Software engineering >  Convert flat array into a 3d array creating new grouping rows on each non-numeric value
Convert flat array into a 3d array creating new grouping rows on each non-numeric value

Time:10-15

I need to group values in my flat array so that each non-integer value starts a new group/row in my result array and every integer value encountered until the next occurring non-integer should be pushed into a subarray in the respective group/row.

Input:

$unitsList = [
    "Aulas Gratuitas",
    "149",
    "151",
    "153",
    "Módulo 0",
    "964",
    "989",
    "Módulo 1",
    "985",
    "1079",
    "1001",
    "1003"
    "Módulo 2",
    "1009"
];

Current code:

$newArr = array();
foreach( $unitsList as $item ) {
    if( !is_numeric($item) ) {
        $title = array( "title" => $item, "units" => "" );
        array_push( $newArr, $title);
    } elseif ( is_numeric($item) ) {
        $number = $item;
        array_push( $newArr, $number);
    }
}

Current result:

[
    [
        "title" => "Aulas Gratuitas",
        "units" => ""
    ]
    "149",
    "151",
    "153",
    [
        "title" => "Módulo 0",
        "units" => ""
    ],
    "964",
    "989",
    [
        "title" => 'Módulo 1',
        "units" => ""
    ],
    "985",
    "1079",
    "1001",
    "1003"
    [
        "title" => 'Módulo 2',
        "units" => ''
    ],
    "1009"
]

As you can see, I am having a hard time adding the numbers into the "units" key.

Desired result:

[
    [
        "title" => "Aulas Gratuitas",
        "units" => ["149", "151", "153"]
    ],
    [
        "title" => 'Módulo 0',
        "units" => ["964", "989"]
    ],
    [
        "title" => 'Módulo 1',
        "units" => ["985", "1079", "1001", "1003"]
    ],
    [
        "title" => "Módulo 2",
        "units" => ["1009"]
    ]
]

CodePudding user response:

$originalArray = ['a', 1, 2, 3, 'b', 4, 5, 6];

function formatArray($input) {
   $output = [];

   foreach($input as $inputRow) {
       if (is_string($inputRow)) {
           $output[] = ['title' => $inputRow, 'units' => []];
       } else if (count($output)) {
           $output[count($output)-1]['units'][] = $inputRow;
       }
   }

   return $output;
}


var_dump(formatArray($originalArray));

Note that your numbers after the title CANNOT be strings, otherwise this function will recognize it as new titles.

This code will output:

array(2) {
  [0]=>
  array(2) {
    ["title"]=>
    string(1) "a"
    ["units"]=>
    array(3) {
      [0]=>
      int(1)
      [1]=>
      int(2)
      [2]=>
      int(3)
    }
  }
  [1]=>
  array(2) {
    ["title"]=>
    string(1) "b"
    ["units"]=>
    array(3) {
      [0]=>
      int(4)
      [1]=>
      int(5)
      [2]=>
      int(6)
    }
  }
}

CodePudding user response:

Items in the given list aren't regular. The first item has three units, and the second has two units. We cannot convert them into the expected structure without controlling the type of each item. My solution is below. I added explanations as a comment line.

$values = array(
    "string",
    11111,
    22222,
    33333,
    "string_2",
    44444,
    55555
);

$formattedArray = [];

$index = -1;

foreach ($values as $value) {

    // If the value is a string, create the new array structure item and assign the title
    if (is_string($value)) {
        $index  ;
        $formattedArray[$index]['title'] = $value;
        $formattedArray[$index]['units'] = [];

        // The rest of the code in "foreach scope" is for integer values, so skip the remaining part
        continue;
    }

    // If the following line is executing, the value is an integer
    // Push the value to the current item's units
    $formattedArray[$index]['units'][] = $value;
}

var_dump($formattedArray);

CodePudding user response:

So that you don't need to keep track of first level indexes, use a reference variable and push related data into that AFTER pushing the new row into the result array.

Code: (Demo)

$result = [];
foreach ($array as $value) {
    if (!ctype_digit($value)) {
        unset($units);
        $units = [];
        $result[] = ['title' => $value, 'units' => &$units];
    } else {
        $units[] = $value;
    }
}
var_export($result);
  • unset() is used to destroy the reference to the previous group, otherwise newly pushed data would be sent to two (or more) places in the result array.

  • Reference variables have a default value of null. If your title values are guaranteed to be followed by an integer/unit value, then $units = []; can be removed. If you have a title and then another title there will be no data for the former title group. With the explicit array declaration, the group will have an empty units array instead of null.

Related questions answered with the same general technique:

  • Related