soyaslim Posted April 27, 2023 Share Posted April 27, 2023 Hello guys, How r we? I am trying to manipulate multi-dimension array to the desired array. So, I have this array $products that have three `elements`. Lets say the element as`product` so, three products. $products = [ [ 'test' => [ 'make' => 'test make', 'vehicle_info' => [ '123' => [ 'brake' => 'brake 123 value' ], '234' => [ 'brake' => 'brake 234 value' ] ] ], 'test2' => [ 'vehicle_info' => [ '456' => [ 'brake' => 'brake 456 value' ], '678' => [ 'brake' => 'brake 678 value' ] ] ] ], [ 'test' => [ 'make' => 'test make', 'vehicle_info' => [ '234' => [ 'brake' => 'brake 234 value' ] ] ], 'test2' => [ 'vehicle_info' => [ '456' => [ 'brake' => 'brake 456 value' ] ] ], 'test3' => [ 'vehicle_info' => [ '789' => [ 'brake' => 'brake 789 value' ] ] ] ], [ 'test' => [ 'make' => 'test make', 'vehicle_info' => [ '234' => [ 'brake' => 'brake 234 value' ] ] ], 'test3' => [ 'vehicle_info' => [ '789' => [ 'brake' => 'brake 789 value' ] ] ] ] ]; So, here is the desired output of the common array between 3 products. Since, the key `test` is common in all three products and `234` is common within `test` array in all three products. Array ( [test] => Array ( [make] => test make [vehicle_info] => Array ( [234] => Array ( [brake] => brake 234 value ) ) ) ) So, the solution I have works fine but I want to optimize my code or to improve it as much as possible. Please let me know if the steps are not clear. $commonArray = []; $total = count($products) - 1; $vehicles = $products[0]; foreach ($vehicles as $key => $vehicle) { foreach ($vehicle['vehicle_info'] as $ktype => $fitments) { $countKey = 0; // keep track of key exists in all products // loop through all the products foreach ($products as $index => $product) { if ($index == 0) // skip the first product continue; foreach ($product as $k => $pro) { if ($key == $k) { foreach ($pro['vehicle_info'] as $kt => $fits) { if ($ktype == $kt) { $countKey++; if ($countKey == $total) { // check if the key exists in all products $commonArray[$key] = $pro; } } } } } } } } print_r($commonArray); Quote Link to comment https://forums.phpfreaks.com/topic/316211-manipulating-multi-dimensional-array/ Share on other sites More sharing options...
requinix Posted April 27, 2023 Share Posted April 27, 2023 All you really need is some sort of "array_intersect_recursive", because the exact structure of the $products doesn't actually change how you compare the different parts of it to others, but unfortunately PHP doesn't have that built-in and implementing it yourself is a bit complicated. A quick improvement is that you can array_shift($products) to remove the first one from the array and return it back to you. Means you don't need to skip $index 0 anymore. A more complicated improvement would be to adjust your approach: rather than count how many times something appears before adding it, it would save some processing time if you immediately bailed out when you discovered that something did not appear. As in: $commonArray = []; $first = array_shift($products); foreach ($first as $key => $vehicle) { $commonVehicle = []; // check if $key exists in all the other $products // if not, use continue (likely a continue 2; or continue 3;) to skip ahead of this loop here and go to the next $key // if so, build up the contents of $commonVehicle... if ($commonVehicle) { // only add this $key if there is something to add $commonArray[$key] = $vehicle; } } And like I said, while the keys of $products changes as you dig into it, you're repeating the same "check if <key> exists in other <arrays>" all the way down, so you'd basically just repeat the above code a couple more times with different variable names. Quote Link to comment https://forums.phpfreaks.com/topic/316211-manipulating-multi-dimensional-array/#findComment-1607751 Share on other sites More sharing options...
soyaslim Posted April 27, 2023 Author Share Posted April 27, 2023 56 minutes ago, requinix said: All you really need is some sort of "array_intersect_recursive", because the exact structure of the $products doesn't actually change how you compare the different parts of it to others, but unfortunately PHP doesn't have that built-in and implementing it yourself is a bit complicated. A quick improvement is that you can array_shift($products) to remove the first one from the array and return it back to you. Means you don't need to skip $index 0 anymore. A more complicated improvement would be to adjust your approach: rather than count how many times something appears before adding it, it would save some processing time if you immediately bailed out when you discovered that something did not appear. As in: $commonArray = []; $first = array_shift($products); foreach ($first as $key => $vehicle) { $commonVehicle = []; // check if $key exists in all the other $products // if not, use continue (likely a continue 2; or continue 3;) to skip ahead of this loop here and go to the next $key // if so, build up the contents of $commonVehicle... if ($commonVehicle) { // only add this $key if there is something to add $commonArray[$key] = $vehicle; } } And like I said, while the keys of $products changes as you dig into it, you're repeating the same "check if <key> exists in other <arrays>" all the way down, so you'd basically just repeat the above code a couple more times with different variable names. Hi @requinix Thanks for your feedback and suggestion and clean code. However, I need to check the vehicle info`key` which is `123`, `234` so on as well before adding to commonArray. I added the condition to your code like below. But it only check the `key` but not the vehicle info `key`. $commonArray = []; $first = array_shift($products); foreach ($first as $key => $vehicle) { $commonVehicle = []; foreach ($products as $product) { foreach ($product as $k => $pro) { // check if $key exists in all the other $products if ($key == $k) { // if so, build up the contents of $commonVehicle... $commonVehicle[$key] = $pro; } else { // if not, use continue (likely a continue 2; or continue 3;) to skip ahead of this loop here and go to the next $key continue 2; } } } if ($commonVehicle) { // only add this $key if there is something to add $commonArray[$key] = $vehicle; } } print_r($commonArray); Quote Link to comment https://forums.phpfreaks.com/topic/316211-manipulating-multi-dimensional-array/#findComment-1607752 Share on other sites More sharing options...
requinix Posted April 27, 2023 Share Posted April 27, 2023 I'll try to explain a little better about the approach I'm thinking of. It's recursive. You start looking at the outermost values - the "test" keys in your example - and you decide whether they should be included in the common array. Should they? Only if they're present in the other arrays. So you look at the other arrays. If they don't have a "test" then you already know the answer (no) and you can skip ahead to the next key. If so, you know that the key is present but you don't know if the corresponding value (the vehicle information) is the same; if it's partly the same, you just keep the common parts. Next you look at that array's values - the "make" and "vehicle_info" keys - and you decide whether they should be included in this inner common array. Should they? Only if they're present in the other "test" sub-arrays. So you look at the other sub-arrays. If they don't have the "make"/"vehicle_info"/whatever then you already know the answer (no) and you can skip ahead to the next sub-key. If so, you know that the key is present but you don't know if the corresponding value (the make, or vehicle info array) is the same; the make is a string so you can compare that, while if the vehicle info is partly the same, you just keep the common parts. See how that's working? The same overall approach applies to the "test" parts, as well as the "make" and "vehicle_info" parts, as well as the actual vehicle parts themselves, as well as the data about the vehicle parts. At each of those different "layers", when you have an array to compare, you need to delve into that array to see what may be common with the other usages. The main point to illustrate was the ability to stop early: if you're looking for some particular key, like the "make", then you naturally have to check the other arrays to see if they have it, but if they don't then you don't have to keep looking any further. And the technique for "stopping early" is that you (a) have this array of "common" parts you build up, as you confirm that each piece of data exists in the other arrays, and (b) at the end of whatever loop, you take that common array and add it to whatever the "parent" thing is (be that the big common array of data, or some sub-array of vehicles or parts or whatever), and you can use a continue as you're checking things to skip over that and immediately start checking the next key. However, Working through this now, I'm seeing that it's not as effective as I thought it might be, and that the code will be nicer if it took a slightly different approach: do the whole "stop early" thing, but instead of building up the common data, try tearing down the uncommon data. There's also not so much a need to continue; anymore - as in, do something to skip past some code and restart a loop - because there's no dangling code. Could break; though, but I opted for using if/else blocks instead to make it easier to follow. $vehicleGroups = [...]; $firstGroup = array_shift($vehicleGroups); $commonGroup = $firstGroup; foreach ($firstGroup as $id => $vehicle) { foreach ($vehicleGroups as $otherGroup) { // layer 1: the vehicle IDs // check if $id exists in all the other groups if (!isset($otherGroup[$id])) { // if not, tear down unset($commonGroup[$id]); } else { // if so, go deeper $otherVehicle = $otherGroup[$id]; // for convenience, to pair with $vehicle // layer 2: the make and vehicle_info // check if make exists and is in all the other groups if (isset($vehicle["make"])) { if (!isset($otherVehicle["make"])) { // if not, tear down unset($commonGroup[$id]["make"]); } else { // nothing to potentially go deeper into } } // same for vehicle_info if (isset($vehicle["vehicle_info"])) { if (!isset($otherVehicle["vehicle_info"])) { // if not, tear down unset($commonGroup[$id]["vehicle_info"]); } else { // layer 3: the vehicle_infos foreach ($vehicle["vehicle_info"] as $infoid => $info) { // check if $infoid exists in all the other groups if (!isset($otherVehicle["vehicle_info"][$infoid])) { // if not, tear down unset($commonGroup[$id]["vehicle_info"][$infoid]); } else { // if so, go deeper // layer 4: the bits of data in the vehicle_infos // I'm going to assume that "brake" is only one of many possible values // rather than handle each one like make/vehicle_info did individually, do another foreach foreach ($vehicle["vehicle_info"][$infoid] as $partid => $part) { // check if $partid exists in all the other groups if (!isset($otherVehicle["vehicle_info"][$infoid][$partid])) { // if not, tear down unset($commonGroup[$id]["vehicle_info"][$infoid][$partid]); } else { // nothing to potentially go deeper into...? } } } // don't keep the info if there was nothing in common if (!$commonGroup[$id]["vehicle_info"][$infoid]) { unset($commonGroup[$id]["vehicle_info"][$infoid]); } } } // don't keep vehicle_info if there was nothing in common if (!$commonGroup[$id]["vehicle_info"]) { unset($commonGroup[$id]["vehicle_info"]); } } // don't keep it if there was nothing in common if (!$commonGroup[$id]) { unset($commonGroup[$id]); } } } } print_r($commonGroup); Note the similarities in the different "layers", and how they all repeat the same idea more or less the same way: if the thing is not present then remove, otherwise check it in more detail. Is it better, or even shorter or simpler, than what you have? Not really. Could it be improved? Undoubtedly. But the point was to show the "you can stop early if you know it won't work" concept. Quote Link to comment https://forums.phpfreaks.com/topic/316211-manipulating-multi-dimensional-array/#findComment-1607753 Share on other sites More sharing options...
Solution soyaslim Posted April 27, 2023 Author Solution Share Posted April 27, 2023 (edited) Hi @requinix , Thank you for your explanation and for taking the time to solve this problem. The whole recursive approach I really do not follow that method because I find it really confusing and hard, however, I am able to break it through my existing solution. Below I have simply break the loop if the `vehicle_info` `key` does not match $commonArray = []; $total = count($products) - 1; $vehicles = $products[0]; array_shift($products); foreach ($vehicles as $key => $vehicle) { foreach ($vehicle['vehicle_info'] as $ktype => $fitments) { $countKey = 0; // check all the products foreach ($products as $index => $product) { if (!isset($product[$key]['vehicle_info'])) { // key not found in this product, move on to next key break 2; } foreach ($product[$key]['vehicle_info'] as $kt => $fits) { if ($ktype == $kt) { $countKey++; if ($countKey == $total) { $commonArray[$key] = $product[$key]; } } } } } } print_r($commonArray); Edited April 27, 2023 by soyaslim Quote Link to comment https://forums.phpfreaks.com/topic/316211-manipulating-multi-dimensional-array/#findComment-1607755 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.