converting plain array to multidimensional one using recursion - php

everyone!
I'm stuck trying write a recursive function. =(
This is my function, which, as I expected, will turn my plain array into multidimensional one.
function BuildTree($src, $index=0) {
foreach ($src as $index=>$curentItem) {
$nextItem = (is_array($src[$index+1]))?$src[$index+1]:false;
unset($src[$index]);
if ($nextItem['d']==$curentItem['d']) $brunchArray[] = $curentItem['n'];
if ($nextItem['d']>$curentItem['d']) $brunchArray['childrens'] = BuildTree($src, $index);
if (!$nextItem || $nextItem['d']<$curentItem['d']) return $brunchArray;
}
}
Input array is something like this:
$input = array (
array(
'n' => 'Articles',
'd' => 0
),
array(
'n' => 'Article 1',
'd' => 1
),
array(
'n' => 'Books',
'd' => 0
),
array(
'n' => 'Book 1',
'd' => 1
),
array(
'n' => 'Book 2',
'd' => 1
),
array(
'n' => 'Chapter 1',
'd' => 2
),
array(
'n' => 'Chapter 2',
'd' => 2
)
);
And I want it to be converted into this:
array (
array(
'n' => 'Articles',
'd' => 0,
'childrens' => array (
array(
'n' => 'Article 1',
'd' => 1
),
)
),
array(
'n' => 'Books',
'd' => 0,
'childrens' => array (
array(
'n' => 'Book 1',
'd' => 1
),
array(
'n' => 'Book 2',
'd' => 1
'childrens' => array (
array(
'n' => 'Chapter 1',
'd' => 2
),
array(
'n' => 'Chapter 2',
'd' => 2
)
)
)
)
)
)
I already spent three hours trying to solve this. =( Any help will be highly appreciated!

Here is a solution without recursion:
function convert($arr) {
$stack = array();
$output = array();
$arr[] = array('d' => -1); // Dummy record at the end
for($i = 0; $i < count($arr); $i++) {
while(!empty($stack) && $stack[count($stack) - 1]['d'] > $arr[$i]['d']) {
$current_d = $stack[count($stack) - 1]['d'];
$children = array();
while(!empty($stack) && $stack[count($stack) - 1]['d'] >= $current_d) {
$children[] = array_pop($stack);
}
$children = array_reverse($children);
if(empty($stack)) {
foreach($children as $child) {
$output[] = $child;
}
} else {
$stack[count($stack) - 1]['children'] = $children;
}
}
$stack[] = $arr[$i];
}
return $output;
}
$input = array (
array(
'n' => 'Articles',
'd' => 0
),
array(
'n' => 'Article 1',
'd' => 1
),
array(
'n' => 'Books',
'd' => 0
),
array(
'n' => 'Book 1',
'd' => 1
),
array(
'n' => 'Book 2',
'd' => 1
),
array(
'n' => 'Chapter 1',
'd' => 2
),
array(
'n' => 'Chapter 2',
'd' => 2
)
);
var_dump(convert($input));

Using the same $input:
$output = array();
function buildTree(&$input, &$output, &$current, $level = 0) {
if(!$input)
return;
$next = array_shift($input);
if($next['d'] == $level) {
$current[] = $next;
return buildTree($input, $output, $current, $level);
} else if($next['d'] == $level + 1) {
$current[count($current) - 1]['childrens'] = array($next);
return buildTree($input, $output, $current[count($current) - 1]['childrens'], $level + 1);
} else {
$output[] = $next;
return buildTree($input, $output, $output, 0);
}
}
buildTree($input, $output, $output);
var_dump($output);

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;
}

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'];

Nested SQL resulting to Nested List

Any ideas on how can I generate a nested list without the need to recreate a lot of select statements?
I'm currently using this code
<ol>
<?php
$getparents=mysql_query("select id,subject from list");
while($parent=mysql_fetch_assoc($getparents)){
?>
<li><?php echo $parent["id"];?></li>
<?php
$childsparent=$parent["id"];
$getchild=mysql_query("select id,subject from list where parent_id='".$childsparent."'");
if (!mysql_num_rows($getchild){
echo '</ol>';
}
else
{
echo '<ol>';
while ($child=mysql_fetch_assoc($getchild)){
echo '<li>'.$child["subject"].'</li>';
}
$childsparent=$child["id"];
}
?>
</ol>
Is there a way to stop the while from getting all results and check a result first if it has child nests before it moves forward?
The result should be something like
1.
2.
2.1
2.1.1
2.2
3
I found this function I wrote some time ago. I think it is the kind of thing you want. You just need to change the logic to print out rather than store to an array:
function nestify( $arrs, $depth_key = 'depth' )
{
$nested = array();
$depths = array();
foreach( $arrs as $key => $arr ) {
if( $arr[$depth_key] == 0 ) {
$nested[$key] = $arr;
$depths[$arr[$depth_key] + 1] = $key;
}
else {
$parent =& $nested;
for( $i = 1; $i <= ( $arr[$depth_key] ); $i++ ) {
$parent =& $parent[$depths[$i]];
}
$parent[$key] = $arr;
$depths[$arr[$depth_key] + 1] = $key;
}
}
return $nested;
}
$arr = array(
array( 'name' => 'Joe Splogs', 'depth' => 0 ),
array( 'name' => 'ProSplogger', 'depth' => 0 ),
array( 'name' => 'Pinky Lyres', 'depth' => 1 ),
array( 'name' => 'Pseudologia fantastica', 'depth' => 2 ),
array( 'name' => 'TextLinkBarry', 'depth' => 1 ),
array( 'name' => 'Foo bar Jones', 'depth' => 0 )
);
$new = nestify( $arr, 'depth' );
#-> Returns
array (
'0' => array
(
'name' => 'Joe Splogs',
'depth' => 0
),
'1' => array
(
'name' => 'ProSplogger',
'depth' => 0
'2' => array
(
'name' => 'Pinky Lyres',
'depth' => 1
'3' => array
(
'name' => 'Pseudologia fantastica',
'depth' => 2
),
),
'4' => array
(
'name' => 'TextLinkBarry',
'depth' => 1
),
),
'5' => array
(
'name' => 'Foo bar Jones',
'depth' => 0
),
);

Convert 2d array into 3d with 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.)

Categories