How to merge and unique multidimention array

Viewed 67

I'm not familiar with PHP language. I need to merge accounts data if name is matched and filter out duplicated data that provided in array structure.

I'm trying to merge the accounts value then use array_unique to remain the value is unique but i'm not sure where to write this logic.

$array =

array:2 [
  0 => array:2 [
    "name" => "account.payroll"
    "accounts" => array:3 [
      0 => array:3 [
        "id" => 27
        "code" => "700000"
      ]
      1 => array:3 [
        "id" => 101
        "code" => "701000"
      ]
    ]
   
    1 => array:2 [
    "name" => "account.payroll"
    "accounts" => array:3 [
      0 => array:3 [
        "id" => 27
        "code" => "700000"
      ]
      1 => array:3 [
        "id" => 102
        "code" => "702000"
      ]
    ]
  ]

Expected output

array:1 [
  0 => array:3 [
    "name" => "account.payroll"
    "accounts" => array:7 [
      0 => array:3 [
        "id" => 27
        "code" => "700000"
      ]
      1 => array:3 [
        "id" => 101
        "code" => "701000"
      ]
      2 => array:3 [
        "id" => 102
        "code" => "702000"
      ]
    ]
  ]
]

My code

array_map(function($tmp) { 
                unset($tmp['total_net_movement']);
                if (isset($tmp['accounts'])) {
                    foreach ($tmp['accounts'] as $key => $value) {
                        unset($value['net_movement']); 
                        $tmp['accounts'][$key] = $value;
                    }
                }               
                return $tmp; 
            
            }, $array);
3 Answers

The expected array has the structure of the input array[0]. This is used as a basis. The "accounts" are all collected with array_merge(). array_unique() filters out all duplicate entries.

$newData = $input[0];

foreach($input as $i => $dataItem){
  if($i == 0) continue;
  $newData['accounts'] = array_merge($newData['accounts'],$dataItem['accounts']);
}

$newData['accounts'] = array_values(array_unique($newData['accounts'],SORT_REGULAR));
$newData = [$newData];

Assuming the account names ($array[*]['name']) are simple (eg, alphanumeric) and short, we can index the new array with it for faster/simpler lookup. We can also do the same for the accounts ($array[*]['accounts']).

$newArray = [];

foreach ($array as $accountType) {
    // If the account name (eg, $newArray['account.payroll']) does not exist, create/insert it
    if (!isset($newArray[$accountType['name']])) {
        $newArray[$accountType['name']] = $accountType;
        $newArray[$accountType['name']]['accounts'] = [];
    }

    // If the accounts under said name (eg, $newArray['account.payroll']['accounts'][27]) does not exist, create/insert it
    foreach ($accountType['accounts'] as $account) {
        if (!isset($newArray[$accountType['name']]['accounts'][$account['id']])) {
            $newArray[$accountType['name']]['accounts'][$account['id']] = $account;
        }
    }
}

var_dump($newArray);
// [
//     'account.payroll' => [
//         'name' => 'account.payroll',
//         'accounts' => [
//             27 => [
//                 'id' => 27,
//                 'code' => '700000',
//             ],
//             101 => [
//                 'id' => 101,
//                 'code' => '701000',
//             ],
//             102 => [
//                 'id' => 102,
//                 'code' => '702000',
//             ],
//         ],
//     ],
// ]

// If for some reason we want the account types and accounts to only be indexed with zero-based sequential integer
$newArray = array_values($newArray);
foreach ($newArray as &$accountType) {
    $accountType['accounts'] = array_values($accountType['accounts']);
}
unset($accountType);

// Or
$newArray = array_values($newArray);
array_walk($newArray, function (&$accountType) {
    $accountType['accounts'] = array_values($accountType['accounts']);
});

var_dump($newArray);
// [
//     0 => [
//         'name' => 'account.payroll',
//         'accounts' => [
//             0 => [
//                 'id' => 27,
//                 'code' => '700000',
//             ],
//             1 => [
//                 'id' => 101,
//                 'code' => '701000',
//             ],
//             2 => [
//                 'id' => 102,
//                 'code' => '702000',
//             ],
//         ],
//     ],
// ]

The following code snippet should do the trick.

<?php

declare(strict_types=1);

$array = [
    [
        'name'     => 'account.payroll',
        'accounts' => [
            [
                'id'   => 27,
                'code' => '700000',
            ],
            [
                'id'   => 101,
                'code' => '701000',
            ],
        ],
    ],
    [
        'name'     => 'account.payroll',
        'accounts' => [
            [
                'id'   => 27,
                'code' => '700000',
            ],
            [
                'id'   => 102,
                'code' => '702000',
            ],
        ],
    ],
];

$result = [];

foreach ($array as $element) {
    if (!\array_key_exists($element['name'], $result)) {
        $result[$element['name']] = [
            'name'     => $element['name'],
            'accounts' => [],
        ];
    }

    foreach ($element['accounts'] as $account) {
        if (!\array_key_exists($account['id'], $result[$element['name']]['accounts'])) {
            $result[$element['name']]['accounts'][$account['id']] = $account;
        }
    }
}

$result = array_values($result);

foreach ($result as $key => $value) {
    $result[$key]['accounts'] = array_values($value['accounts']);
}

var_dump($result);

The output would be:

array(1) {
  [0]=>
  array(2) {
    ["name"]=>
    string(15) "account.payroll"
    ["accounts"]=>
    array(3) {
      [0]=>
      array(2) {
        ["id"]=>
        int(27)
        ["code"]=>
        string(6) "700000"
      }
      [1]=>
      array(2) {
        ["id"]=>
        int(101)
        ["code"]=>
        string(6) "701000"
      }
      [2]=>
      array(2) {
        ["id"]=>
        int(102)
        ["code"]=>
        string(6) "702000"
      }
    }
  }
}
Related