Create multidimensional array recursively - php

The original array looks like this:
$array = array(
array(
'key' => 'key1',
'val' => 'val1'
),
array(
'key' => 'key2:subkey1',
'val' => 'val2'
),
array(
'key' => 'key3:subkey2',
'val' => 'val3'
),
array(
'key' => 'key3:subkey3:subsubkey1',
'val' => 'val4'
),
array(
'key' => 'key3:subkey3:subsubkey2',
'val' => 'val5'
),
array(
'key' => 'key3:subkey3:subsubkey3',
'val' => 'val6'
)
);
And I want to generate a new array based on the original one that looks like this:
$result = array(
'key1' => 'val1',
'key2' => array(
'subkey1' => 'val2'
),
'key3' => array(
'subkey2' => 'val3',
'subkey3' => array(
'subsubkey1' => 'val4',
'subsubkey2' => 'val5',
'subsubkey3' => 'val6'
)
)
);
The algorithm should be able to handle a reference array of any depth.
What I have tried so far works, but I am not happy with using eval for various reasons:
function convert($array) {
$out = array();
foreach ($array as $data) {
$key = $data['key'];
$pos = strpos($key, ':');
if ($pos === false) {
$out[$key] = $data['val'];
} else {
$split = explode(":", $key);
eval("\$out['" . implode("']['", $split) . "'] = '" . $data['val'] . "';");
}
}
return $out;
}
Is there a way to solve this without resorting to eval, i.e. setting the $out directly? The val comes from user input, so it is obviously very unsafe to use eval in this case.
Thank you for your advice.

function convert($array) {
$out = array();
foreach ($array as $data) {
$key = $data['key'];
$value = $data['val'];
$helper = &$out;
foreach (explode(':', $key) as $i) {
$helper = &$helper[$i];
}
$helper = $value;
}
return $out;
}
Took me a while to figure out how to do it. References are perhaps the best way to achieve it.
The $helper-variable is a temporary reference to the $out-array we want to return after we're done. It splits each key by the colon as delimiter and iterates through each individual key. In every iteration we change the helper reference to the next key.
An example:
$out = array();
explode(':', $key) => array('key3', 'subkey3', 'subsubkey1');
$helper = &$out;
// foreach loop starts
$helper = $helper['key3']; // Iteration 1
$helper = $helper['key3']['subkey3']; // Iteration 2
$helper = $helper['key3']['subkey3']['subsubkey1']; // Iteration 3
I hope you understand how it works.

Try this neat code:
$array = array(
array(
'key' => 'key1',
'val' => 'val1'
),
array(
'key' => 'key2:subkey1',
'val' => 'val2'
),
array(
'key' => 'key3:subkey2',
'val' => 'val3'
),
array(
'key' => 'key3:subkey3:subsubkey1',
'val' => 'val4'
),
array(
'key' => 'key3:subkey3:subsubkey2',
'val' => 'val5'
),
array(
'key' => 'key3:subkey3:subsubkey3',
'val' => 'val6'
)
);
$new_array = array();
foreach($array as $element) {
$temp = &$new_array;
foreach(explode(':', $element['key']) as $key) {
$temp = &$temp[$key];
}
$temp = $element['val'];
}
unset($temp);
var_dump($new_array);

Related

Convert all values into a single string in a multi-dimension PHP array?

$array = array(
array(
'id' => 1,
'first_name' => 'testOne',
),
array(
'id' => 333,
'first_name' => 'test333',
)
);
Required Output:
array("1_testOne","333_test333");
This should help -
array_map(function($a) {
return implode('_', $a); // implode all with '_'
}, $array);
array_map()
$array = array(
array(
'id' => 1,
'first_name' => 'testOne',
),
array(
'id' => 333,
'first_name' => 'test333',
)
);
$new_array = [];
foreach($array as $key => $value) {
$new_array[] = $value['id'] . "_" . $value['first_name'];
}
echo "<pre>";
print_r($new_array);
It will return output as
Array('1_testOne', '333_test333')

Recursive get path from multidimensional php array

I have array:
$adm_menu_old = array (
array(
'id' => 1,
'name' => 'Test1',
),
array(
'id' => 3,
'name' => 'Test3',
'childrens' => array(
array(
'id' => 31,
'name' => 'Test31',
),
array(
'id' => 32,
'name' => 'Test32',
'childrens' => array(
array(
'id' => 321,
'name' => 'Test321',
),
),
)
),
array(
'id' => 4,
'name' => 'Test4',
),
);
Say i know id value.
I need get path with all parents of this id.
For example i need get path to this element: id=321
i need get array with key name values:
array('Test3','Test32','Test321')
how should look like recursive function?
Try this function:
function getNames($id, $arr) {
$result = array();
foreach($arr as $key => $val) {
if(is_array($val)) {
if($val["id"] == $id) {
$result[] = $val["name"];
} elseif(!empty($val["childrens"]) && is_array($val["childrens"])) {
$sub_res = getNames($id, $val["childrens"]);
if(count($sub_res) > 0) {
$result[] = $val["name"];
$result = array_merge($result, $sub_res);
}
}
}
}
return $result;
}

How to change the index name of array

I have a multidimensional array,
I'm recursively changing values of array with I need.
It is working for keys which are not array.
But not for keys which are array.
How can I change value of one to test like "one" => "test",
$arr = array(
'one' => array(
array('something' => 'value'),
array('something2' => 'value2'),
'another' => 'anothervalue'
),
'two' => array(
array('something' => 'value'),
array('something2' => 'value2'),
'another' => 'anothervalue'
)
);
function update_something(&$item, $key)
{
if($key == 'one')
$item = 'test';
}
array_walk_recursive($arr, 'update_something');
EXPECTED ARRAY STRUCTURE IS
array(
'one' => 'test',
'two' => array(
array('something' => 'value'),
array('something2' => 'value2'),
'another' => 'anothervalue'
)
);
UPDATE2
$html_structure = array(
array(
'tag' => 'div',
'class' => 'lines',
array(
'tag' => 'div',
'one' => array(
'tag' => 'div',
array(
'tag' => 'span',
'style' => 'margin:10px; padding:10px',
'key' => 'title',
),
'key' => 'subject',
)
)
)
);
UPDATE3
$array = array(
array(
'tag' => 'div',
'class' => 'lines',
array(
'tag' => 'div',
'repeat' => array(
'tag' => 'div',
array(
'tag' => 'span',
'style' => 'margin:10px; padding:10px',
'key' => 'title',
),
'key' => 'subject',
)
)
)
);
function update_recursively($array, $key = '', $value = array()) {
//print_r($array); print_r($value);
foreach ($array as $k => $v) {
if ($k === $key){
$array[$k] = $value;
}
elseif (is_array($v))
$array[$k] = update_recursively($v);
}
return $array;
}
print_r(update_recursively($array, 'repeat', array('d' => 'a')));
If you array isn't too large, something like this would work:
function update_recursively($array) {
foreach ($array as $k => $v) {
if ($k === 'one')
$array[$k] = 'test';
elseif (is_array($v))
$array[$k] = update_recursively($v);
}
return $array;
}
$updated_arr = update_recurisvely($arr);
But you need to be a bit careful if it's really big as it can get slow and memory intensive. Note that it won't update your old array, like array_walk_recursive would, it will return an updated version instead.
* UPDATE *
Version that handles the Update3 scenario where we specify key to look for and value to replace it with.
function update_recursively($array, $key = '', $value = array()) {
foreach ($array as $k => $v) {
if ($k === $key)
$array[$k] = $value;
elseif (is_array($v))
$array[$k] = update_recursively($v, $key, $value);
}
return $array;
}
You can try:
function update_something(&$item, $key) {
if($key == 'one') {
array_splice($item,0,'test');
}
}

How to unflatten array in PHP using dot notation

I have php array structure like this:
array(
'servicemanagement.scheduler.events.edit' => 'Edit',
'servicemanagement.scheduler.events.delete' => 'Delete',
'servicemanagement.scheduler.events' => 'Events',
'servicemanagement.scheduler' => 'Scheduler',
'servicemanagement.subscribers' => 'Subscribers',
'servicemanagement.subscribers.index' => 'Index',
'servicemanagement' => 'Service management',
);
And I would like to convert is to multidimensional array like:
array(
'servicemanagement' => array(
'id' => 'servicemanagement',
'title' => 'Service Management',
'children' => array(
'scheduler' => array(
'id' => 'servicemanagement.scheduler',
'title' => 'Scheduler',
'children' => array(
'events' => array(
'id' => 'servicemanagement.scheduler.events',
'title' => 'Events',
'children' => array(
'edit' => array(
'id' => 'servicemanagement.scheduler.events.edit',
'title' => 'Edit',
'children' => array(),
),
'delete' => array(
'id' => 'servicemanagement.scheduler.events.delete',
'title' => 'Delete',
'children' => array(),
),
),
),
),
),
'subscribers' => array(
'id' => 'servicemanagement.subscribers',
'title' => 'Subscribers',
'children' => array(
'index' => array(
'id' => 'servicemanagement.subscribers.index',
'title' => 'Index',
)
),
),
),
),
);
I have checked some answers already like this one:
How to set a deep array in PHP
But it seems that i could not manage to clear up the writing on top of the arrays and the last record 'servicemanagement' removes all of the previous records.
The function that is used there is
function setArray(&$array, $keys, $value) {
$keys = explode(".", $keys);
$current = &$array;
foreach($keys as $key) {
$current = &$current[$key];
}
$current = $value;
}
Another function that I have found but it is not doing the expected result is:
function unflatten($array,$prefix = '')
{
$result = array();
foreach($array as $key=>$value) {
if (!empty($prefix)) {
$key = preg_replace('#^'.preg_quote($prefix).'#','',$key);
}
if (strpos($key,'.') !== false) {
parse_str('result['.str_replace('.','][',$key)."]=".$value);
} else {
$result[$key] = $value;
}
}
return $result;
}
It is an option to use recursion to unflatten this array since the end format is the same for all records.
May anyone give me a tip ot this one?
I created an unflatten function for reference here:
https://gist.github.com/Gerst20051/b14c05b72c73b49bc2d306e7c8b86223
$results = [
'id' => 'abc123',
'address.id' => 'def456',
'address.coordinates.lat' => '12.345',
'address.coordinates.lng' => '67.89',
'address.coordinates.geo.accurate' => true,
];
function unflatten($data) {
$output = [];
foreach ($data as $key => $value) {
$parts = explode('.', $key);
$nested = &$output;
while (count($parts) > 1) {
$nested = &$nested[array_shift($parts)];
if (!is_array($nested)) $nested = [];
}
$nested[array_shift($parts)] = $value;
}
return $output;
}
echo json_encode(unflatten($results));
/*
{
"id": "abc123",
"address": {
"id": "def456",
"coordinates": {
"lat": "12.345",
"lng": "67.89",
"geo": {
"accurate": true
}
}
}
}
*/
This was slightly influenced by the following resources:
https://gist.github.com/tanftw/8f159fec2c898af0163f
https://medium.com/#assertchris/dot-notation-3fd3e42edc61
This isn't the cleanest solution but it works as a single function
$your_array = array(
'servicemanagement.scheduler.events.edit' => 'Edit',
'servicemanagement.scheduler.events.delete' => 'Delete',
'servicemanagement.scheduler.events' => 'Events',
'servicemanagement.scheduler' => 'Scheduler',
'servicemanagement.subscribers' => 'Subscribers',
'servicemanagement.subscribers.index' => 'Index',
'servicemanagement' => 'Service management',
);
function expand($array, $level = 0)
{
$result = array();
$next = $level + 1;
foreach($array as $key=>$value) {
$tree = explode('.', $key);
if(isset($tree[$level])) {
if(!isset($tree[$next])) {
$result[$tree[$level]]['id'] = $key;
$result[$tree[$level]]['title'] = $value;
if(!isset($result[$tree[$level]]['children'])) {
$result[$tree[$level]]['children'] = array();
}
} else {
if(isset($result[$tree[$level]]['children'])) {
$result[$tree[$level]]['children'] = array_merge_recursive($result[$tree[$level]]['children'], expand(array($key => $value), $next));
} else {
$result[$tree[$level]]['children'] = expand(array($key => $value), $next);
}
}
}
}
return $result;
}
var_export(expand($your_array));

Remove elements from array1 that are in array2

I have arrays that looks like this:
$array1 = array(
'peter#example.com' => array(
'peter' => 'Smith',
),
'john#example.com' => array(
'john' => 'Smith',
),
'louis#example.com' => array(
'louis' => 'Smith',
),
'jane#example.com' => array(
'jane' => 'Smith',
),
);
$array2 = array(
'0' => 'peter#example.com',
'1' => 'john#example.com',
);
How do I remove the array elements in array1 that match array2?
As simple as:
$diff = array_diff_key($array1, array_flip($array2));
Quick and easy (but not as quick and easy as deceze's method, lol)
foreach ($array1 as $key => $value) {
for ($i = 0; $i < count($array2); $i++) {
if ($key == $array2[$i]) {
unset($array1[$key]);
}
}
}

Categories