Convert 2d array into 3d with PHP - php

There are simple 2d array with some sort of tree like this:
node1
node2
node3
It's structure is:
array(
array (
'id' : 1,
'pid': 0,
'title' : 'node1',
'level' : 1
),
array (
'id' : 2,
'pid': 1,
'title' : 'node2',
'level' : 2
),
array (
'id' : 3,
'pid': 2,
'title' : 'node3',
'level' : 3
),
)
Is there solutions with PHP to convert this array into:
array(
array (
'id' : 1,
'title' : 'node1',
'child' : array (
'id' : 2,
'title' : 'node2',
'child' : array (
'id' : 3,
'title' : 'node3',
),
),
)
...
)

Found #SO PHP Traversing Function to turn single array into nested array with children - based on parent id
$inArray = array(
array('ID' => '1', 'parentcat_ID' => '0'),
array('ID' => '2', 'parentcat_ID' => '0'),
array('ID' => '6', 'parentcat_ID' => '1'),
array('ID' => '7', 'parentcat_ID' => '1'),
array('ID' => '8', 'parentcat_ID' => '6'),
array('ID' => '9', 'parentcat_ID' => '1'),
array('ID' => '13', 'parentcat_ID' => '7'),
array('ID' => '14', 'parentcat_ID' => '8'),
);
function makeParentChildRelations(&$inArray, &$outArray, $currentParentId = 0) {
if(!is_array($inArray)) {
return;
}
if(!is_array($outArray)) {
return;
}
foreach($inArray as $key => $tuple) {
if($tuple['parentcat_ID'] == $currentParentId) {
$tuple['children'] = array();
makeParentChildRelations($inArray, $tuple['children'], $tuple['ID']);
$outArray[] = $tuple;
}
}
}
$outArray = array();
makeParentChildRelations($inArray, $outArray);
print_r($outArray);

<?php
$p = array(0 => array());
foreach($nodes as $n)
{
$pid = $n['pid'];
$id = $n['id'];
if (!isset($p[$pid]))
$p[$pid] = array('child' => array());
if (isset($p[$id]))
$child = &$p[$id]['child'];
else
$child = array();
$p[$id] = $n;
$p[$id]['child'] = &$child;
unset($p[$id]['pid']);
unset($p[$id]['level']);
unset($child);
$p[$pid]['child'][] = &$p[$id];
// $p[$pid]['child'] = &$p[$id]; // or this, if only one child
}
$nodes = $p['0']['child'];
unset($p);
?>
If each node can only have one child, then replace the one line with $p[$pid]['child'] = &$p[$id];.
(Edit: fixed it to work regardless of how the nodes are sorted.)

Related

Combinations array into array in PHP

I will try to explain everything :)
I have 2 arrays:
$packs = array(
array(
'name' => 'Pack 1',
'zones' => array(
array('zone' => 2),
array('zone' => 2),
)
),
array(
'name' => 'Pack 2',
'zones' => array(
array('zone' => 2),
array('zone' => 2),
array('zone' => 2),
)
),
array(
'name' => 'Pack 3',
'zones' => array(
array('zone' => 2),
array('zone' => 3),
)
),
array(
'name' => 'Pack 4',
'zones' => array(
array('zone' => 3),
array('zone' => 3),
)
)
);And products:
$products = array(
array(
'id' => '1',
'zone' => '2'
),
array(
'id' => '8',
'zone' => '2'
),
array(
'id' => '13',
'zone' => '3'
),
array(
'id' => '11',
'zone' => '2'
),
array(
'id' => '10',
'zone' => '2'
),
array(
'id' => '12',
'zone' => '3'
)
);
Then I would like to have all $packs zones combination with those products zones.
For example, products_zones are 2, 2, 3, 2, 2, 3 then I would like something like this:
Packs:
Combination 1:
Pack 1 (when Pack zones are in Products Zones, add pack to combination and remove products from array)
Pack 1
Combination 2:
Pack 1
Pack 3
Combination 3:
Pack 1
Pack 4
Combination 4:
Pack 2
Pack 4
Combination 5:
Pack 2
Pack 2
Pack 4
I'm trying to do that with recursive function but doesn't work. I leave here a link with code:
function search_recursive( $packs = array(), $products = array(), $packs_in = array(), $pass = null ) {
foreach ($packs as $index => $pack) {
// Get zones to compare
$arr_zones = array_column($pack['zonas'], 'zona');
$products_zones = array_column($products, 'zone');
// Check if pack zones are in product zones
$cheak_arr = [];
$arr_zones_temp = $arr_zones;
foreach ($products_zones as $index2 => $temp_zone) {
if ( in_array($temp_zone, $arr_zones_temp) ) {
$cheak_arr[] = $temp_zone;
foreach ($arr_zones_temp as $key => $value) {
if ( $value == $temp_zone ) {
unset($arr_zones_temp[$key]);
break;
}
}
}
}
if ( count($arr_zones) == count($cheak_arr) ) {
// I create a index for first time
$custom_index = ($pass == null) ? $index : $pass;
// Add pack to array if pack zones are in product zones
if ( !isset($packs_in[$custom_index]) ) {
$packs_in[$custom_index] = [
'packs' => array($pack)
];
}
else {
$packs_in[$custom_index]['packs'][] = $pack;
}
// Remove products that have zones same in pack
$temp_prod = $products;
foreach ($arr_zones as $zone) {
foreach ($temp_prod as $key => $value) {
if ( $value['zone'] == $zone ) {
unset($temp_prod[$key]);
break;
}
}
}
if ( $pass != null ) {
$products = $temp_prod;
}
if ( !empty($temp_prod) ) {
// Call myself with less products and index defined
$packs_in = search_recursive( $packs, $temp_prod, $packs_in, $custom_index );
}
else if ( $pass != null ) {
break;
}
}
}
return $packs_in;
}
I think I got what you need, but I'm still a bit confused. Anyway, I suggest you to simplify you "packs" array like my code below:
<?php
$packs = array(
array(
'name' => 'Pack 1',
'zones' => array(2,2),
),
array(
'name' => 'Pack 2',
'zones' => array(2,2,2),
),
array(
'name' => 'Pack 3',
'zones' => array(2,3),
),
array(
'name' => 'Pack 4',
'zones' => array(3,3),
)
);
$products = array(
array(
'id' => '8',
'zone' => '2'
),
array(
'id' => '13',
'zone' => '3'
),
array(
'id' => '11',
'zone' => '2'
),
array(
'id' => '10',
'zone' => '2'
),
array(
'id' => '12',
'zone' => '3'
)
);
$product_zones = array_column($products, 'zone');
//let's order an change to a sequence of numbers like 22233
sort($product_zones);
$product_zones = join('', $product_zones);
$combinations = [];
//here we iterate through all packs 1->2,3,4; 2->3,4 3->4 to find if it matches
foreach($packs as $k => $pack) {
// use k+1 if you can't match the pack with itself
for ($i = $k, $c = count ($packs); $i < $c; $i++) {
//here we do the same as before to combine the packs as string, ex.: 2223
$pack_zones = array_merge($pack['zones'], $packs[$i]['zones']);
sort($pack_zones);
$pack_zones = join('', $pack_zones);
//if it's a substring of our product zones then we have a valid combination
if (strpos($product_zones, $pack_zones) !== false) {
$combinations[] = [$pack['name'], $packs[$i]['name']];
}
}
}
print_r($combinations);
result: 1,3 (22223) ; 1,4 (2233) ; 2,4 (22233) ; 3,3 (2233)

Create an array regrouping all possibilities of another multidimentionnal array

I have small difficulties to convert an array as I want, need some help from pros.
I have an array like that :
$inputs = array(
'size' => array(
's' => 's',
'm' => 'm',
'l' => 'l',
),
'color' => array(
'red' => 'red',
'blue' => 'blue',
),
'option' => 'option 1',
);
From this values, I need to create an array that combine all possibilities, like that :
$possibilities = array(
0 => array('size' => 's', 'color' => 'red', 'option' => 'option 1'),
1 => array('size' => 'm', 'color' => 'red', 'option' => 'option 1'),
2 => array('size' => 'l', 'color' => 'red', 'option' => 'option 1'),
3 => array('size' => 's', 'color' => 'blue', 'option' => 'option 1'),
4 => array('size' => 'm', 'color' => 'blue', 'option' => 'option 1'),
5 => array('size' => 'l', 'color' => 'blue', 'option' => 'option 1'),
);
I precise that I'm on laravel so I can use the collection methods, but even with this helpers methods, I can't find a way to obtain the $possibilities array I want.
The original array is dynamic (can have more options with different label names), so I need something that is able to work no matter the size of the array neither the name of the labels.
Please try this, I hope it'll help you.
<?php
$inputs = array(
'size' => array(
's' => 's',
'm' => 'm',
'l' => 'l',
),
'color' => array(
'red' => 'red',
'blue' => 'blue',
),
'option' => 'option 1',
);
$combinations = [[]];
$length = count($inputs);
foreach (array_keys($inputs) as $key) {
$tmp = [];
foreach ($combinations as $v1) {
if (is_array($inputs[$key])) {
foreach ($inputs[$key] as $v2) {
$tmp[] = array_merge($v1, [$key => $v2]);
}
} else {
$tmp[] = array_merge($v1, [$key => $inputs[$key]]);
}
}
$combinations = $tmp;
}
echo "<pre>";
print_r($combinations);
?>
$output = array();
foreach($inputs['size'] as $size)
{
foreach($inputs['colour'] as $colour)
{
foreach($inputs['option'] as $option)
{
$output[] = array('size' => $size, 'colour' => $colour, 'option' => $option);
}
}
}
return $output;
Update :
$collection = collect($inputs[0]);
array_shift($inputs);
$matrix = $collection->crossJoin($inputs);
$matrix->all();
Update :
$collection = collect(array_shift($inputs));
$matrix = $collection->crossJoin($inputs);
$matrix->all();
Update :
use this function
function combinations($arrays, $i = 0) {
if (!isset($arrays[$i])) {
return array();
}
if ($i == count($arrays) - 1) {
return $arrays[$i];
}
// get combinations from subsequent arrays
$tmp = combinations($arrays, $i + 1);
$result = array();
// concat each array from tmp with each element from $arrays[$i]
foreach ($arrays[$i] as $v) {
foreach ($tmp as $t) {
$result[] = is_array($t) ?
array_merge(array($v), $t) :
array($v, $t);
}
}
return $result;
}

Search value in array and if not found added

I have an array with the following format:
$array = Array(
Array('id' => 77, 'title' => 'title'),
Array('id' => 43, 'title' => 'title2'),
Array('id' => null, 'title' => 'title3'),
Array('id' => null, 'title' => null),
);
This array is populated dynamically, this is just an example. Also, i have a second array with the format:
$searchingArray = Array('43', '5');
The main idea is to search if values from $searchingArray are in $array and if not exists then added. My function is:
function addId($id, $ignoreIfFound=false) {
foreach ($array as $values) {
if ($values['id'] and $values['id'] == $id) {
if (!$ignoreIfFound) {
$array[] = Array('id' => $id, 'title' => 'test5');
break;
}
else{
// do nothing
}
}else{
$array[] = Array('id' => $id, 'title' => 'test5');
break;
}
}
}
foreach ($searchingArray as $id) {
$this->addId($id, true);
}
For given example the result should be:
$array = Array(
Array('id' => 77, 'title' => 'title'),
Array('id' => 43, 'title' => 'title2'),
Array('id' => null, 'title' => 'title3'),
Array('id' => null, 'title' => null),
Array('id' => 5, 'title' => 'test5'),
);
Can you tell me what it is wrong with my code?
This should work for you:
First extract the id column out of your array with array_column(). After this simply loop through your search array and check with in_array() if they id already exists or not. If not simply add it to the array.
$ids = array_column($array, "id");
foreach($searchingArray as $search) {
if(!in_array($search, $ids)) {
$array[] = ["id" => $search, "title" => "title" . $search];
}
}

How to copy this portion of the array to a new array in php?

I have this php array X.
X= array(
'Parent' => array(
'title' => '123',
)
)
I have this php array Y.
Y = array(
'Parent' => array(
'id' => '16',
'title' => 'T1',
),
'Children' => array(
(int) 0 => array(
'id' => '8',
'serial_no' => '1',
),
(int) 1 => array(
'id' => '9',
'serial_no' => '2',
),
(int) 2 => array(
'id' => '14',
'serial_no' => '6',
)
)
)
I want to copy the Children of array Y to the parent of array X to form array Z such that it looks like this;
Z= array(
'Parent' => array(
'title' => '123',
)
'Children' => array(
(int) 0 => array(
'serial_no' => '1'
),
(int) 1 => array(
'serial_no' => '2'
),
(int) 2 => array(
'serial_no' => '6'
)
)
)
Please note that the id key-value pair was removed from the Children of array Y.
I wrote some code of my own.
$Z = array();
$i=0;
foreach($Y as $temp)
{
$Z['Children'][$i] = $temp['Children'][$i];
unset($Z['Children'][$i]['id'];
$i++;
}
$Z['Parent']=$temp['Parent'];
Unfortunately, there is an undefined index error. How can this be done in php? Forget about my code if there are better approaches.
Actually your approach works too, but you need to iterate over sub-array:
$Z = array();
$i=0;
foreach($Y['Children'] as $temp)
{
$Z['Children'][$i] = $temp;
unset($Z['Children'][$i]['id'];
$i++;
}
or what I may do:
$Z = $X;
$Z['Children'] = array();
foreach ( $Y['Children'] as $child ) {
$Z['Children'][] = array(
'serial_no' => $child['serial_no'],
);
}
You can do like.
$Z = array();
foreach($Y['Children'] as $temp)
{
$Z['Children'][] = array('serial_no' => $temp['serial_no']);
}
$Z['Parent']=$X['Parent'];

Group multidimensional array data based on two column values and sum values of one column in each group

I have an array which is created as a combination of two database queries from two separate databases, it looks similar to:
$arr1 = [
['part' => '1', 'address' => 'aaa', 'type' => '1', 'count' => 5],
['part' => '1', 'address' => 'bbb', 'type' => '1', 'count' => 5],
['part' => '1', 'address' => 'ccc', 'type' => '1', 'count' => 5],
['part' => '2', 'address' => 'aaa', 'type' => '1', 'count' => 5],
['part' => '2', 'address' => 'bbb', 'type' => '1', 'count' => 5],
['part' => '2', 'address' => 'ccc', 'type' => '2', 'count' => 5]
];
I am looking for a way to group this array by part and type values. I also need to know the total of the count values as they are grouped.
The results would be something like:
$arr2 = [
['part' => '1', 'type' => '1', 'count' => 15],
['part' => '2', 'type' => '1', 'count' => 10],
['part' => '2', 'type' => '2', 'count' => 5]
];
but I just can't see how to do this. I have seen a few examples of grouping by a single key/value, but not by multiple values at once.
This function should do the job.
function groupByPartAndType($input) {
$output = Array();
foreach($input as $value) {
$output_element = &$output[$value['part'] . "_" . $value['type']];
$output_element['part'] = $value['part'];
$output_element['type'] = $value['type'];
!isset($output_element['count']) && $output_element['count'] = 0;
$output_element['count'] += $value['count'];
}
return array_values($output);
}
If both databases are on the same database server you would be able to do this using SQLs GROUP BY feature.
The following:
$arr2 = array();
foreach ($arr1 as $a) {
unset($a['address']);
$key = $a['type'] . '-' . $a['part'];
if (isset($arr2[$key])) {
$arr2[$key]['count'] += $a['count'];
} else {
$arr2[$key] = $a;
}
}
$arr2 = array_values($arr2);
Would output
array
0 =>
array
'part' => string '1' (length=1)
'type' => string '1' (length=1)
'count' => int 15
1 =>
array
'part' => string '2' (length=1)
'type' => string '1' (length=1)
'count' => int 10
2 =>
array
'part' => string '2' (length=1)
'type' => string '2' (length=1)
'count' => int 5
Something like
$newarr=array();
foreach ( $arr as $Key => $Value ) {
$newarr[$Value[part]][]=$arr[$key];
}
foreach ( $newarr[part] as $Key => $Value ) {
...
}
Full answer for multi-keys arrays grouping is
// * $arr - associative multi keys data array
// * $group_by_fields - array of fields to group by
// * $sum_by_fields - array of fields to calculate sum in group
function array_group_by($arr, $group_by_fields = false, $sum_by_fields = false) {
if ( empty($group_by_fields) ) return; // * nothing to group
$fld_count = 'grp:count'; // * field for count of grouped records in each record group
// * format sum by
if (!empty($sum_by_fields) && !is_array($sum_by_fields)) {
$sum_by_fields = array($sum_by_fields);
}
// * protected from collecting
$fields_collected = array();
// * do
$out = array();
foreach($arr as $value) {
$newval = array();
$key = '';
foreach ($group_by_fields as $field) {
$key .= $value[$field].'_';
$newval[$field] = $value[$field];
unset($value[$field]);
}
// * format key
$key = substr($key,0,-1);
// * count
if (isset($out[$key])) { // * record already exists
$out[$key][$fld_count]++;
} else {
$out[$key] = $newval;
$out[$key][$fld_count]=1;
}
$newval = $out[$key];
// * sum by
if (!empty($sum_by_fields)) {
foreach ($sum_by_fields as $sum_field) {
if (!isset($newval[$sum_field])) $newval[$sum_field] = 0;
$newval[$sum_field] += $value[$sum_field];
unset($value[$sum_field]);
}
}
// * collect differencies
if (!empty($value))
foreach ($value as $field=>$v) if (!is_null($v)) {
if (!is_array($v)) {
$newval[$field][$v] = $v;
} else $newval[$field][join('_', $v)] = $v; // * array values
}
$out[$key] = $newval;
}
return array_values($out);
}
If this task was necessary in one of my projects, I would craft a snippet that would not need reference variables or any iterated function calls.
Inside the loop, declare the composite temporary key as a variable (since it is used more than once). Push the new row into the result array using the composite key as the temporary first-level key.
Use the null coalescing operator to use the pre-existing count for a given group or zero if the group has not yet been encountered. Then add the new count value to the previously accumulated count.
This technique will unconditionally overwrite the encountered group every time it is repeated. In doing so, the data will be updated with the correct part, type, and count values throughout the iterative process.
When the loop finishes, re-index the result array by calling array_values().
Code: (Demo)
$arr1 = [
['part' => '1', 'address' => 'aaa', 'type' => '1', 'count' => 5],
['part' => '1', 'address' => 'bbb', 'type' => '1', 'count' => 5],
['part' => '1', 'address' => 'ccc', 'type' => '1', 'count' => 5],
['part' => '2', 'address' => 'aaa', 'type' => '1', 'count' => 5],
['part' => '2', 'address' => 'bbb', 'type' => '1', 'count' => 5],
['part' => '2', 'address' => 'ccc', 'type' => '2', 'count' => 5]
];
$result = [];
foreach ($arr1 as $row) {
$compositeKey = $row['part'] . '-' . $row['type'];
$result[$compositeKey] = [
'part' => $row['part'],
'type' => $row['type'],
'count' => ($result[$compositeKey]['count'] ?? 0) + $row['count']
];
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'part' => '1',
'type' => '1',
'count' => 15,
),
1 =>
array (
'part' => '2',
'type' => '1',
'count' => 10,
),
2 =>
array (
'part' => '2',
'type' => '2',
'count' => 5,
),
)
p.s. Ideally, this task probably could/should be performed in the sql but we don't have the details to provide any specific guidance.

Categories