PHP explode strings in array to multidimensional array - php

I've seen many questions around this topic, but not any near the case I have.
I have like a really simple folder path as key and want to make the array into a multidimensional array.
My current array
[
'projects' => 'A path',
'projects/project-a' => 'Another path',
'projects/project-b' => 'Yet another path',
'about/me/and/someone/else' => 'Path about me'
]
This is the result I try to get:
[
'projects' => [
'path' => 'A path',
'children' => [
'project-a' => [
'path' => 'Another path'
],
'project-b' => [
'path' => 'Yet another path'
]
]
],
'about' => [
'children' => [
'me' => [
'children' => [
'and' => [
'children' => [
'someone' => [
'children' => [
'else' => [
'path' => 'Path about me'
]
]
]
]
]
]
]
]
]
]
Maybe I can use array_walk_recursive somehow. I know explode can be used to split the parts by /.
Notes
projects/project-a does not have children.
about and all children except the last one does not have a path.
The depth of the array is unknown.

$result = [];
foreach($arr as $k=>$v) {
$path = explode('/', $k);
// temporary array for one path
$temp = [];
// Pointer, used to add a next level
$p = &$temp;
// Save the last part of path
$last = array_pop($path);
foreach($path as $s) {
// Make level upto the last
$p[$s] = ['children' => []];
$p = &$p[$s]['children'];
}
// Add a value
$p[$last] = ['path' => $v];
$result = array_merge_recursive($result, $temp);
}
print_r($result);
demo

Related

How to convert an array as follows?

I have an array
[
[
'title' => 'title0',
'data' => 'data0'
],
[
'title' => 'title1',
'data' => 'data1'
]
]
I need to get the output
[
'title' => ['title0','title1'],
'data' => ['data0', 'data1']
]
Please tell me how can I do this?
You didn't show any attempt, but I'm bored. Just loop the keys from the first sub-array and extract that column. No need to know what the keys are:
foreach(array_keys(reset($array)) as $key) {
$result[$key] = array_column($array, $key);
}
You could also do it this way:
foreach(reset($array) as $key => $val) {
$result[$key] = array_column($array, $key);
}
Or if it's as simple as those two known keys:
$result = ['title' => array_column($array, 'title'),
'data' => array_column($array, 'data')
];
You can do it like this
<?php
$shortedArray = [
'title' => [],
'data' => []
];
$mainArray = [
[
'title' => 'test0',
'data' => 'data0'
],
[
'title' => 'test1',
'data' => 'data1'
]
];
// Loop thru it
foreach($mainArray as $row){
$shortedArray['title'][] = $row['title'];
$shortedArray['data'][] = $row['data'];
}
print_r($shortedArray);
Hope this resolves your issue. Any query, let me know

Removing part of an array from a multidimensional nested array

I've a four levels of nested array like this:
$array = [
[
'website' => [
'id' => 'one'
],
'children' => [
[
'website' => [
'id' => 'one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.one.one.two'
],
'children' => []
]
]
],
[
'website' => [
'id' => 'one.one.two'
],
'children' => [
[
'website' => [
'id' => 'one.one.two.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.one.two.two'
],
'children' => []
]
]
]
]
],
[
'website' => [
'id' => 'one.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.one'
],
'children' => [
[
'website' => [
'id' => 'one.two.one.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.two.one.two'
],
'children' => []
]
]
],
[
'website' => [
'id' => 'one.two.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.two.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.two.two.two'
],
'children' => []
]
]
]
]
]
]
]
];
now, I'd like to remove some of the array element based on some rules. For simplicity, let's say, we'd like to remove array elements that has 'id' equals to one.one.two or one.two.one.
I was looking into some answers provided on stackoverflow and was trying to apply them to solve my issue, but didn't go much. The most chalanging thing is to unset part of an array where it's not the farthest point in the array.
I was trying this to note at which level I want to delete the arrays and then trying to unset them using those array index.
$pointer = [];
foreach($array as $key => $level1) {
$level1pointer = $key;
$pointer[] = markToDelete($level1, $level1pointer);
foreach($level1['children'] as $key => $level2) {
$level2pointer = $key;
$pointer[] = markToDelete($level2, $level1pointer, $level2pointer);
foreach($level2['children'] as $key => $level3) {
$level3pointer = $key;
$pointer[] = markToDelete($level3, $level1pointer, $level2pointer, $level3pointer);
foreach($level3['children'] as $key => $level4) {
$level4pointer = $key;
$pointer[] = markToDelete($level4, $level1pointer, $level2pointer, $level3pointer, $level4pointer);
}
}
}
}
function markToDelete($array, $level1 = null, $level2 = null, $level3 = null, $level4 = null) {
$exclusionList = [
'one.one.two',
'one.two.one'
];
if (!empty($array['website']) && in_array($array['website']['id'], $exclusionList)) {
print_r('marking for deletion: '. $array['website']['id'] . PHP_EOL);
return [
'id' => $array['website']['id'],
'level1' => $level1,
'level2' => $level2,
'level3' => $level3,
'level4' => $level4
];
}
return [];
}
I was also trying to use Iterator like this:
$it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array), \RecursiveIteratorIterator::LEAVES_ONLY);
$newArray = [];
foreach($it as $key => $value) {
$exclusionList = [
'one.one.two',
'one.two.one'
];
if(!in_array($value, $exclusionList)) {
print_r(sprintf('value: %s is ready to be deleted', $value).PHP_EOL);
$newArray[] = $value;
}
}
but I need a way to unset the array when looping through the iterator.
I'd like to get an output like this:
$array = [
[
'website' => [
'id' => 'one'
],
'children' => [
[
'website' => [
'id' => 'one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.one.one.two'
],
'children' => []
]
]
],
]
],
[
'website' => [
'id' => 'one.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.two.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.two.two.two'
],
'children' => []
]
]
]
]
]
]
]
];
I'd really appreciate help on how to resolve this in more efficient way. Thanks.
Here's a recursive function that will do what you want. It traverses the array, looking for children whose website id is in the list of ids to be removed and unsetting them. Note that because of your additional top-level array, you need to iterate the function over those values.
function delete_entries(&$array, $ids_to_delete) {
foreach ($array['children'] as $index => &$child) {
if (in_array($child['website']['id'], $ids_to_delete)) {
unset($array['children'][$index]);
}
delete_entries($child, $ids_to_delete);
}
}
foreach ($array as &$arr) {
delete_entries($arr, array('one.one.two', 'one.two.one'));
}
var_export($array);
The output is as you desire but too long to reproduce here. See the demo on 3v4l.org
Update
The above code won't delete entries on the top level because the array structure is different from lower levels in the array. That can be dealt with in the outer foreach loop:
$excluded = array('two', 'one.one.two', 'one.two.one');
foreach ($array as $key => &$arr) {
if (in_array($arr['website']['id'], $excluded)) {
unset($array[$key]);
}
else {
delete_entries($arr, $excluded);
}
}
Updated demo

add key value pair to dynamic multidimensional array in php

I have a multidimensional array as per below example which I want to search through for a specific value $needle. If this value is found I want to add a new key=> value pair for each node of the array branch where the $needle is subsequently found.
The needle should be unique but the depth of the sub arrays is dynamic.
Example:
$data= [
0=> [
'title' => 'alpha',
'children' => [],
],
1 =>[
'title' => 'beta',
'children' => [
0=>[
'title' => 'charlie',
'children' => [],
],
1=>[
'title' => 'delta',
'children' => [],
],
2=>[
'title' => 'echo',
'children' => [
0=>[
'title' => 'foxtrot',
'children' => [],
],
],
],
],
],
2 => [
'title' => 'gulf',
'children' => []
]
Now when I search for $needle= foxtrot I want to add to each of the subarrays "active" => 1
e.:
$data= [
0=> ...
1 =>[
'title' => 'beta',
**'active' => 1,**
'children' => [
0=>[
'title' => 'charlie',
'children' => [],
],
1=>[
'title' => 'delta',
'children' => [],
],
2=>[
'title' => 'echo',
**'active' => 1,**
'children' => [
0=>[
'title' => 'foxtrot',
**'active' => 1,**
'children' => [],
],
],
],
],
],
My not working attempt. I don't know how I correctly merge the key => value on each finding:
function array_mod_active($array, $needle, $parent_key='' )
{
foreach($array as $key => $value) {
if (is_array($value)) {
array_mod_active($value, $needle, $key );
} elseif ($value === $needle) {
array_merge(array('active' => 1), $array[$parent_key]);
}
}
return $array;
}
This is one recursive way to do it. I've added inline comments to explain each step.
Code: (Demo)
function keysort($a,$b){ // this function is optional; purely used for aesthetic/readability purposes
$order=['title'=>0,'active'=>1,'children'=>2]; // order keys by this specific order
return $order[$a]<=>$order[$b];
}
function activate_lineage(&$array,$needle){
foreach($array as &$item){
if($item['title']==$needle){ // $needle located, make active
$item['active']=1; // add active element
uksort($item,'keysort'); // optional call
return $array; // send full updated array back where it came from
}elseif(!empty($item['children'])){ // drill down if there are any children
$copy=$item; // preserve original (this avoids a post-recursion column-search)
$item['children']=activate_lineage($item['children'],$needle); // recurse
if($copy!==$item){ // if children subarray was updated, a child was activated
$item['active']=1; // add active element
uksort($item,'keysort'); // optional call
break; // don't continue the loop; only one parent will be activated (per level)
}
}
}
return $array; // return the full array, no matter what level it is on
}
// to call:
var_export(activate_lineage($data,'foxtrot'));
/* or because the function modifies by reference...
activate_lineage($data,'foxtrot');
var_export($data);
*/

Recursively Create an Array from another Array

I am trying to make a multi-dimensional array build an array path adding the hr field so it looks like this:
I just can't figure out how to add the totals, nor create a sub-array so the dot notation in an option too. My goal is to get something like this:
[1] => [1][2][1][5][0][6] = 35 (the second child path "1")
[1] => [1][2][1][5][0][7] = 25
or Something like this:
array (
[children.0.children.0.children.0.total] = 20
[children.0.children.1.children.1.total] = 35
// etc
)
The complicated part is that it goes in different directions and I want to know what is the highest and lowest total based on the path:
==> Run Code Here or Copy/Paste
// -------------
// The Flattener
// -------------
function doit($myArray) {
$iter = new RecursiveIteratorIterator(new RecursiveArrayIterator($myArray));
$result = array();
foreach ($iter as $leafKey => $leafValue) {
$keys = array();
foreach (range(0, $iter->getDepth()) as $depth) {
$keys[] = $iter->getSubIterator($depth)->key();
}
$result[ join('.', $keys) ] = $leafValue;
}
return $result;
}
// -------------
// Example Tree
// -------------
$tree = [
'id' => 1,
'type' => 'note',
'data' => [],
'children' => [
[
'id' => 2,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => [
[
'id' => 3,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => [
'id' => 4,
'type' => 'exit',
'data' => [],
'children' => []
]
],
[
'id' => 5,
'type' => 'note',
'data' => [
'hr' => 10,
],
'children' => [
[
'id' => 6,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => []
],
[
'id' => 7,
'type' => 'exit',
'data' => [],
'children' => []
],
]
]
],
]
]
];
$result = doit($tree);
print_r($result);
This seems to work, I found it somewhere googling all day.
array_reduce(array_reverse($keys), function($parent_array, $key) {
return $parent_array ? [$key => $parent_array] : [$key];
}, null);

Efficient way to extract a part of multidimensional array

I have an array which looks something like this:
$a = [
1 => [
'title' => 'test',
'items' => [
5 => [
'title' => 'hello',
'items' => []
]
]
],
2 => [
'title' => 'second',
'items' => [
7 => [
'title' => 'hello in second',
'items' => []
]
]
],
3 => [
'title' => 'third',
'items' => [
10 => [
'title' => 'hello in third',
'items' => []
]
]
],
];
I need a way to extract parts of it by key no matter where they are in the tree. I've tried several ways but I'm not sure how efficient they are. If it helps I need to extract only those parts which have a numeric key. Any help will be much appreciated.
Try using the SPL iterators:
class KeyFinderFilterIterator extends FilterIterator {
private $search = null;
public function __construct($iterator, $search) {
$this->search = $search;
parent::__construct($iterator);
}
public function accept(){
return $this->key() == $this->search;
}
}
$it = new KeyFinderFilterIterator(
new RecursiveIteratorIterator(
new RecursiveArrayIterator($a),
RecursiveIteratorIterator::SELF_FIRST
),
10
);
foreach ($it as $key => $value) {
var_dump($value);
}

Categories