I have an array I'm trying to change this arrays's some keys but function fails for arrays which are recursive.
What could be problem
Any one can fix this?
$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')));
You forget to pass 2nd and 3rd parameter to inner function call:
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, $key, $value); // Here
}
}
return $array;
}
Related
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);
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');
}
}
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));
This is my code,
and I need your help to understand it.
<?php $tree = array(
array(
'name' => 'Item-1',
'children' => array()),
array('name' => 'Item-2',
'children' => array(
array('name' => 'Item-2-1',
'children' => array()),
)),
array('name' => 'Item-3',
'children' => array(
array('name' => 'Item-3-1',
'children' => array()),
array('name' => 'Item-3-2',
'children' => array(
array('name' => 'Item-3-2-1',
'children' => array()),
array('name' => 'Item-3-2-2',
'children' => array()),
array('name' => 'Item-3-2-3',
'children' => array(
array('name' => 'Item-3-2-3-1',
'children' => array()),
)),
)),
)),
);
What i need is one recursive function, which will return all names (name).
For example:
Item-1
Item-2
Item-2-1
Item-3
Item-3-1
Item-3-2
........
My attempt but it just doesn't seem to work for me
function tree($tree){
foreach ($tree as $key =>$value) {
foreach ($value as $key=>$value){
echo "$value<br>"; }
echo "<br>"; } }
You can use RecursiveArrayIterator and RecursiveIteratorIterator
$ai = new RecursiveIteratorIterator(new RecursiveArrayIterator($tree));
echo "<pre>";
foreach ( $ai as $value ) {
echo $value, PHP_EOL;
}
Output
Item-1
Item-2
Item-2-1
Item-3
Item-3-1
Item-3-2
Item-3-2-1
Item-3-2-2
Item-3-2-3
Item-3-2-3-1
See Live Demo
From http://snipplr.com/view/10200/
// Recursively traverses a multi-dimensional array.
function traverseArray($array)
{
// Loops through each element. If element again is array, function is recalled. If not, result is echoed.
foreach($array as $key=>$value)
{
if(is_array($value))
{
traverseArray($value);
}else{
echo $key." = ".$value."<br />\n";
}
}
}
Also see Sum integers in 2d array using recursion?
I have two arrays...
$arr1 = array(
'name',
'date' => array('default' => '2009-06-13', 'format' => 'short'),
'address',
'zipcode' => array('default' => 12345, 'hidden' => true)
);
$arr2 = array(
'name',
'language',
'date' => array('format' => 'long', 'hidden' => true),
'zipcode' => array('hidden' => false)
);
Here's the desired result:
$final = array(
'name',
'date' => array('default' => '2009-06-13', 'format' => 'long', 'hidden' => true),
'zipcode' => array('default' => 12345, 'hidden' => false)
);
Only the elements from $arr2 (that also exist in $arr1) are used
Each element's attributes are merged
If a common element (e.g. zipcode) shares an attribute (e.g. hidden), then the attribute from $arr2 takes precedence
What are some good approaches for solving this problem?
Thanks in advance.
EDIT: I tried to hobble something together... critiques welcomed:
$new_array = array_intersect_key($arr2, $arr1);
foreach ($new_array as $key => $val)
{
if (is_array($arr1[$key]))
{
if (is_array($val))
{
$new_array[$key] = array_merge($val, $arr1[$key]);
}
else
{
$new_array[$key] = $arr1[$key];
}
}
}
You were close
$newArr = array_intersect_key($arr1, $arr2);
foreach ($newArr as $key => $val)
{
if (is_array($val))
{
$newArr[$key] = array_merge($arr1[$key], $arr2[$key]);
}
}
Edit
Just had to change the array_intersect to array_intersect_key
you might find array_intersect() useful
link text