skip to Main Content

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"]
    ]
]

3

Answers


  1. $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' => []];
           } elseif (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)
        }
      }
    }
    
    
    Login or Signup to reply.
  2. 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);
    
    Login or Signup to reply.
  3. 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:


    If you are running PHP7.3 or higher (and by this point you definitely should be) AND you are intimidated/mystified by using a reference variable, then you can leverage array_key_last() to locate the latest created array row.

    Code: (Demo)

    $result = [];
    foreach ($array as $value) {
        if (!ctype_digit($value)) {
            $result[] = ['title' => $value, 'units' => []];
        } else {
            $result[array_key_last($result)]['units'][] = $value;
        }
    }
    var_export($result);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search