Ikarus
Ikarus

Reputation: 3

Group a 2d array by a column and create subarrays in each group

I am trying to group an array by it's values. Below is my array:

$array = Array
(
    0 => Array
        (
            'name' => 'John Doe',
            'age' => '36',
            'code' => '437',                           
            'group' => '1000',
        ),
    1 => Array
        (
            'name' => 'John Doe',
            'age' => '36',
            'code' => '437',                           
            'group' => '7777',
        ),

    2 => Array
        (
            'name' => 'John Doe',
            'age' => '36',
            'code' => '437',                           
            'group' => '7777',
        ),
    3 => Array
        (
            'name' => 'John Doe',
            'age' => '36',
            'code' => '437',                           
            'group' => '4000',
        ),

    4 => Array
        (
            'name' => 'John Doe',
            'age' => '36',
            'code' => '437',                           
            'group' => '4000',
        ),

    5 => Array
        (
            'name' => 'John Doe',
            'age' => '36',
            'code' => '437',                           
            'group' => '5000',
        ),

    6 => Array
        (
            'name' => 'John Doe',
            'age' => '36',
            'code' => '437',                           
            'group' => '6000',
        ),

    7 => Array
        (
            'name' => 'John Doe',
            'age' => '36',
            'code' => '437',                           
            'group' => '6000',
        ),
);

I have tried below:

$result = array();
    foreach ($array as $key => $record) {
        if (!isset($result[$record['code']])) {
            $result[$record['code']] = array(
                'name' => $record['name'],
                'age' => $record['age'],
                'groups' => array(array($record['code'], $record['group'])),
            );
        }
        else {
            $result[$record['code']]['groups'][] = array($record['code'],$record['group']);
        }
    }
    $result = array_values($result);

    print_r($result);

And I am getting:

Array
(
    [0] => Array
        (
            [name] => John Doe
            [age] => 36
            [groups] => Array
                (
                    [0] => Array
                        (
                            [0] => 437
                            [1] => 1000
                        )

                    [1] => Array
                        (
                            [0] => 437
                            [1] => 7777
                        )

                    [2] => Array
                        (
                            [0] => 437
                            [1] => 7777
                        )

                    [3] => Array
                        (
                            [0] => 437
                            [1] => 4000
                        )

                    [4] => Array
                        (
                            [0] => 437
                            [1] => 4000
                        )

                    [5] => Array
                        (
                            [0] => 437
                            [1] => 5000
                        )

                    [6] => Array
                        (
                            [0] => 437
                            [1] => 6000
                        )

                    [7] => Array
                        (
                            [0] => 437
                            [1] => 6000
                        )

                )

        )

)

Now, I want my array to be grouped by the values of group 7777, 1000 and others(all others group values):

Array
(
    [0] => Array
        (
            [name] => John Doe
            [age] => 36
            [7777] => Array
                (
                    [0] => Array
                        (
                            [0] => 437
                            [1] => 7777
                        )

                    [1] => Array
                        (
                            [0] => 437
                            [1] => 7777
                        )
                )
            [6000] => Array
                (
                    [0] => Array
                        (
                            [0] => 437
                            [1] => 6000
                        )

                    [1] => Array
                        (
                            [0] => 437
                            [1] => 6000
                        )
                )
            [others] => Array
                (
                    [0] => Array
                        (
                            [0] => 437
                            [1] => 1000
                        )

                    [1] => Array
                        (
                            [0] => 437
                            [1] => 4000
                        )

                    [2] => Array
                        (
                            [0] => 437
                            [1] => 4000
                        )

                    [3] => Array
                        (
                            [0] => 437
                            [1] => 5000
                        )
                )

        )

)

Upvotes: 0

Views: 6455

Answers (3)

mickmackusa
mickmackusa

Reputation: 47864

I am not sure what your business logic is, but your data storage design is not optimal. Redundant levels and values can and should be removed as a matter of best practices. That advice aside, Mickael's solution will possibly let you down if your input array is not pre-sorted on code values and you have more than one code value.

The process is very straight forward. Use code values as temporary unique keys in your output array. If dealing with the first occurrence of a code value, set the name and age elements. On every iteration, add a new subarray to the output array based on the code value and the group value condition. When the loop finishes, reindex the output array with array_values().

Code: (Demo)

$array = [
    ['name' => 'John Doe', 'age' => '36', 'code' => '437', 'group' => '1000'],
    ['name' => 'John Doe', 'age' => '36', 'code' => '437', 'group' => '7777'],
    ['name' => 'John Doe', 'age' => '36', 'code' => '437', 'group' => '7777'],
    ['name' => 'John Doe', 'age' => '36', 'code' => '437', 'group' => '4000'],
    ['name' => 'John Doe', 'age' => '36', 'code' => '437', 'group' => '4000'],
    ['name' => 'John Doe', 'age' => '36', 'code' => '437', 'group' => '5000'],
    ['name' => 'John Doe', 'age' => '36', 'code' => '437', 'group' => '6000'],
    ['name' => 'John Doe', 'age' => '36', 'code' => '437', 'group' => '6000']
];

foreach ($array as $row) {
    if (!isset($result[$row['code']])) {
        $result[$row['code']] = ['name' => $row['name'], 'age' => $row['age']];
    }
    $result[$row['code']][in_array($row['group'], ['7777','6000']) ? $row['group'] : 'others'][] = [$row['code'], $row['group']];
}
var_export(array_values($result));

Output:

array (
  0 => 
  array (
    'name' => 'John Doe',
    'age' => '36',
    'others' => 
    array (
      0 => 
      array (
        0 => '437',
        1 => '1000',
      ),
      1 => 
      array (
        0 => '437',
        1 => '4000',
      ),
      2 => 
      array (
        0 => '437',
        1 => '4000',
      ),
      3 => 
      array (
        0 => '437',
        1 => '5000',
      ),
    ),
    7777 => 
    array (
      0 => 
      array (
        0 => '437',
        1 => '7777',
      ),
      1 => 
      array (
        0 => '437',
        1 => '7777',
      ),
    ),
    6000 => 
    array (
      0 => 
      array (
        0 => '437',
        1 => '6000',
      ),
      1 => 
      array (
        0 => '437',
        1 => '6000',
      ),
    ),
  ),
)

Upvotes: 1

Mickaël Leger
Mickaël Leger

Reputation: 3440

Try this :

// Create a new array
$result = array();

// Loop through your array
foreach ($array as $value) {
    // Create a key that start at 0
    $i = 0;
    // Test if $result[0] exist : if yes, you have data, else you have nothing
    if (isset($result[$i])) {
        do {
            // Check if the 'name' is new : if yes, $i++ to check next name
            if ($result[$i]['name'] !== $value['name']) $i++;
            // If you find similar name, stop here
            else break;
        } while (isset($result[$i]));
    // Just add $result[0] with name and age value
    } else {
        $result[$i] = array (
            'name' => $value['name'],
            'age'  => $value['age']
        );
    }
    // Now you know the index of result you need to work with
    // Just add a new code / group array to your code index
    $result[$i][$value['group']][] = array($value['code'], $value['group']);
}

If you do var_dump($result); the output is :

array (size=1)
0 => 
  array (size=7)
    'name' => string 'John Doe' (length=8)
    'age' => string '36' (length=2)
    1000 => 
      array (size=1)
        0 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '1000' (length=4)
    7777 => 
      array (size=2)
        0 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '7777' (length=4)
        1 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '7777' (length=4)
    4000 => 
      array (size=2)
        0 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '4000' (length=4)
        1 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '4000' (length=4)
    5000 => 
      array (size=1)
        0 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '5000' (length=4)
    6000 => 
      array (size=2)
        0 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '6000' (length=4)
        1 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '6000' (length=4)

EDIT :

To get only group value equal to 7777 and 6000 replace

$result[$i][$value['group']][] = array($value['code'], $value['group']);

by

$group_value = $value['group'] == "7777" || $value['group'] == "6000" ? $value['group'] : "Others";
$result[$i][$group_value][] = array($value['code'], $value['group']);

Now the output of var_dump($result); is :

array (size=1)
  0 => 
  array (size=5)
    'name' => string 'John Doe' (length=8)
    'age' => string '36' (length=2)
    'Others' => 
      array (size=4)
        0 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '1000' (length=4)
        1 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '4000' (length=4)
        2 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '4000' (length=4)
        3 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '5000' (length=4)
    7777 => 
      array (size=2)
        0 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '7777' (length=4)
        1 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '7777' (length=4)
    6000 => 
      array (size=2)
        0 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '6000' (length=4)
        1 => 
          array (size=2)
            0 => string '437' (length=3)
            1 => string '6000' (length=4)

Is it what you want?

Upvotes: 0

Andreas
Andreas

Reputation: 23958

Here is one method that heavily relies on array_intersect if I may say it myself.
This is to make sure the number of loops is keept as low as possible.

I loop only the unique names, and the unique groups within that name.

$names = array_column($array, "name");
$groups = array_column($array, "group");

foreach(array_unique($names) as $name){
    $intersects = array_intersect_key($array, array_intersect($names, [$name]));
    $new[$name] = ["name" => $name, "age" => end($intersects)["age"]]; // Create the start of the array
    foreach(array_unique($groups) as $group){ // loop only unique groups
        $intersects = array_intersect_key($array, array_intersect($groups, [$group])); // find matching arrays with this group
        foreach($intersects as $int){
            $temp = array_diff($int, ["name" => $new[$name]["name"], "age" => $new[$name]["age"]]); // remove the name and age from the matching array and place them in 6000/7777 or others
            if($temp["group"] == "6000" || $temp["group"] == "7777"){
                $new[$name][$group][] = $temp; 
            }else{
                $new[$name]["others"][] = $temp;
            }
        }
    }
}
var_dump($new);

output:

array(1) {
  ["John Doe"]=>
  array(5) {
    ["name"]=>
    string(8) "John Doe"
    ["age"]=>
    string(2) "36"
    ["others"]=>
    array(4) {
      [0]=>
      array(2) {
        ["code"]=>
        string(3) "437"
        ["group"]=>
        string(4) "1000"
      }
      [1]=>
      array(2) {
        ["code"]=>
        string(3) "437"
        ["group"]=>
        string(4) "4000"
      }
      [2]=>
      array(2) {
        ["code"]=>
        string(3) "437"
        ["group"]=>
        string(4) "4000"
      }
      [3]=>
      array(2) {
        ["code"]=>
        string(3) "437"
        ["group"]=>
        string(4) "5000"
      }
    }
    [7777]=>
    array(2) {
      [0]=>
      array(2) {
        ["code"]=>
        string(3) "437"
        ["group"]=>
        string(4) "7777"
      }
      [1]=>
      array(2) {
        ["code"]=>
        string(3) "437"
        ["group"]=>
        string(4) "7777"
      }
    }
    [6000]=>
    array(2) {
      [0]=>
      array(2) {
        ["code"]=>
        string(3) "437"
        ["group"]=>
        string(4) "6000"
      }
      [1]=>
      array(2) {
        ["code"]=>
        string(3) "437"
        ["group"]=>
        string(4) "6000"
      }
    }
  }
}

https://3v4l.org/jCKk7

EDIT changed 1000 to 6000 as read in comments to other answer.

Upvotes: 0

Related Questions