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');
}
}
Related
I have this array:
$all = array(
'meat' => Object(
'name' => 'meat',
'color' => 'red',
'class' => 'food'
),
'chicken' => Object(
'name' => 'chicken',
'color' => 'white',
'class' => 'food'
),
'apple' => Object(
'name' => 'apple',
'color' => 'green',
'class' => 'Fruit'
),
'blueberry' => Object(
'name' => 'blueberry',
'color' => 'blue',
'class' => 'Fruit'
)
);
and i want to Sort it and rebuild it to be like this:
$theright = array(
array(
'class' => 'food',
'menu' => array(
array(
'name' => 'meat',
'color' => 'red',
),
array(
'name' => 'chicken',
'color' => 'white',
)
)
),
array(
'class' => 'Fruit',
'menu' => array(
array(
'name' => 'apple',
'color' => 'green',
),
array(
'name' => 'blueberry',
'color' => 'blue',
)
)
)
);
I tried to Collect all classes in$all array then compare each value with $all array:
$classArray = array();
foreach($all as $key => $value) {
$classArray[$value->class] = array();
}
foreach($classArray as $key => $value) {
$theright[] = array('class' => $key, 'menu' => array());
}
this code get me this array:
$theright = array(
array(
'class' => 'food',
'menu' => array()
),
array(
'class' => 'Fruit',
'menu' => array()
)
);
and i stop here , how to complete it ?
You could just use the class as a key to group them together. Example:
$food = array();
// gather class
foreach($all as $item) {
if(!isset($food[$item->class])) {
$food[$item->class] = array(
'class' => $item->class,
'menu' => array(
array(
'name' => $item->name,
'color' => $item->name,
)
)
);
} else {
$food[$item->class]['menu'][] = array('name' => $item->name,'color' => $item->color,);
}
}
// simple reindex
$food = array_values($food);
There is no need for a second loop. This should do what you want.
$classMap = array();
foreach ($all as $item)
{
// check if class has been created in the class map
if ( ! array_key_exists($classMap, $item['class']))
{
$classMap[$item['class']] = array(
'class' => $item['class'],
'menu' => array()
);
}
$classMap[$item['class']]['menu'][] = array(
'name' => $item['name'],
'color' => $item['color']
);
}
Try with less loop counts (2)
$all = [];
function getSelectClassData(array &$all)
{
$finalArr = [];
while (count($all) > 1) {
$res = [];
$class = array_values($all)[0]->class;
$selectedDataArr = getSelectSimilerMenuData($all, $class);
$res['class'] = $class;
$res['menu'] = array_values($selectedDataArr);
$all = array_diff_key($all,array_flip(array_keys($selectedDataArr)));
$finalArr[] = $res;
}
return $finalArr;
}
function getSelectSimilerMenuData(array $all, $class)
{
return array_filter(
$all,
function ($e) use ($class) {
return $e->class == $class;
}
);
}
print_r(getSelectClassData($all));
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 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;
}
I have a function which name is repeat.
I want to repeat array values if array key == 'repeat'
But my if ($k == 'repeat') comparement fails.
What is wrong with my comparement?
function repeat($schema, $repeat = array()){
foreach($schema as $k => $v){
if($k == 'repeat')
3rd line does not working properly inside repeat function.
$schema = array(
array(
'tag' => 'div',
'class' => 'lines',
'repeat' => array(
'tag' => 'div',
array(
'tag' => 'span',
'style' => 'margin:10px; padding:10px',
'key' => 'title',
),
'key' => 'subject',
)
)
);
$repeat = array('Country Name' => 'Usa', 'City Name' => 'Newyork');
// Recursive String Replace - recursive_array_replace(mixed, mixed, array);
function recursive_array_replace($find, $replace, $array){
if (!is_array($array)){
return str_replace($find, $replace, $array);
}
$newArray = array();
foreach ($array as $key => $value) {
$newArray[$key] = recursive_array_replace($find, $replace, $value);
}
return $newArray;
}
function repeat($schema, $repeat = array()){
foreach($schema as $k => $v){
if($k == 'repeat'){
foreach($repeat as $rk => $rv){
$value = recursive_array_replace('title', $rk, $v);
$value = recursive_array_replace('subject', $rv, $value);
$info[] = $value;
}
}
}
//$schema = recursive_array_replace('repeat', $repeat, $schema);
return $info;
}
print_r(repeat($schema, $repeat));
UPDATE1
Schema might be
$schema = array(
'tag' => 'div',
'class' => 'lines',
array(
'tag' => 'div',
'class' => 'lines',
'repeat' => array(
'tag' => 'div',
array(
'tag' => 'span',
'style' => 'margin:10px; padding:10px',
'key' => 'title',
),
'key' => 'subject',
array(
'tag' => 'span',
'style' => 'margin:10px; padding:10px',
'key' => 'title',
),
'key' => 'subject',
)
)
);
UPDATE 2
$schema = 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',
)
)
)
);
In my opinion compartment fails because your $schema variable does not have 'repeat' key.
You do foreach of all items in $schema variable, however (before update) there was only one item: the array. After the update there are three items, having keys 'tag', 'class', and one array which has no key, so it is automatically assigned to 0.
In both cases you are able to reach 'repeat' key by referring to the 0th item of the array (via $schema[0]['repeat']).
If there are no other errors (I haven't checked) you should replace
foreach($schema as $k => $v){
to
foreach($schema[0] as $k => $v){
This will refer to the nested array; however, only in case there are no other numeric keys in the $schema array.
To ensure this you might for example first check which item is array, eg.
foreach($schema as $item){ // this is only an auxiliary loop
if(is_array($item)){
$new_schema=$item;
break;
}
}
and then move to main loop:
foreach($new_schema as $k => $v){
etc.
After UPDATE 2
You need to find your own way to access the 'repeat' key. You may do this iteratively. In your code you access the wrong array, the first level, which has no "repeat" key, but you should go deeper.
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));