How to unflatten array in PHP using dot notation - php

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

Related

Search for an array value with keys from another array

I have two arrays with nearly the same structure.
The first array is $_POST data while the second one holds regex rules and some other stuff for data validation.
Example:
$data = array(
'name' => 'John Doe',
'address' => array(
'city' => 'Somewhere far beyond'
)
);
$structure = array(
'address' => array(
'city' => array(
'regex' => 'someregex'
)
)
);
Now I want to check
$data['address']['city'] with $structure['address']['city']['regex']
or
$data['foo']['bar']['baz']['xyz'] with $structure['foo']['bar']['baz']['xyz']['regex']
Any ideas how to achieve this with a PHP function?
Edit: It seems that I found a solution by myself.
$data = array(
'name' => 'John Doe',
'address' => array(
'city' => 'Somewhere far beyond'
),
'mail' => 'test#test.tld'
);
$structure = array(
'address' => array(
'city' => array(
'regex' => 'some_city_regex1',
)
),
'mail' => array(
'regex' => 'some_mail_regex1',
)
);
function getRegex($data, $structure)
{
$return = false;
foreach ($data as $key => $value) {
if (empty($structure[$key])) {
continue;
}
if (is_array($value) && is_array($structure[$key])) {
getRegex($value, $structure[$key]);
}
else {
if (! empty($structure[$key]['regex'])) {
echo sprintf('Key "%s" with value "%s" will be checked with regex "%s"', $key, $value, $structure[$key]['regex']) . '<br>';
}
}
}
return $return;
}
getRegex($data, $structure);
Given these arrays:
$data = array(
'name' => 'John Doe',
'address' => array(
'city' => 'Somewhere far beyond'
),
'foo' => array(
'bar' => array(
'baz' => array(
'xyz' => 'hij'
)
)
)
);
$structure = array(
'address' => array(
'city' => array(
'regex' => '[a-Z]'
)
),
'foo' => array(
'bar' => array(
'baz' => array(
'xyz' => array(
'regex' => '[0-9]+'
)
)
)
)
);
and this function:
function validate ($data, $structure, &$validated) {
if (is_array($data)) {
foreach ($data as $key => &$value) {
if (
array_key_exists($key, $structure)
and is_array($structure[$key])
) {
if (array_key_exists('regex', $structure[$key])) {
if (!preg_match($structure[$key]['regex'])) {
$validated = false;
}
}
validate($value, $structure[$key], $validated);
}
}
}
}
you can check the arrays against each other and get a validation result like so:
$validated = true;
validate($data, $structure, $validated);
if ($validated) {
echo 'everything validates!';
}
else {
echo 'validation error';
}
Ah, you've found a solution. Very nice.

Sort and merge in a array

I have trouble in handle array PHP, I have an array:
[0] {
'email' => 'test#gmail.com',
'meta' => {
'product' => {
'id' => '1',
'content' => 'This is content'
}
}
}
[1] {
'email' => 'test2#gmail.com',
'meta' => {
'product' => {
'id' => '2',
'content' => 'This is content'
}
}
}
[2] {
'email' => 'test2#gmail.com',
'meta' => {
'product' => {
'id' => '3',
'content' => 'This is content'
}
}
}
I need to merge this array by value 'email', like this:
[0] {
'email' => 'test#gmail.com',
'meta' => {
'product' => {
'id' => '1',
'content' => 'This is content'
}
}
}
[1] {
'email' => 'test2#gmail.com',
'meta' => {
'product' => [0] {
'id' => '2',
'content' => 'This is content'
}
[1] {
'id' => '3',
'content' => 'This is content'
}
}
}
Could somebody help me?
$sorted_array = [];
$emails = [];
$i = 0;
foreach ($arr as $array) {
if(!empty($array['email']) && !empty($array['meta']['product'])){
if( in_array($array['email'], $emails)){
$i--;
} else {
$emails[] = $array['email'];
}
$sorted_array[$i]['email'] = $array['email'];
$sorted_array[$i]['meta']['product'][] = $array['meta']['product'];
$i++;
}
}
echo "<pre>";
print_r($sorted_array);
Hope this will help you
Php has many function to sort an Array. You can consult the documentation to choice the better algorithms to your case http://php.net/manual/en/array.sorting.php. And you can merge result Arrays with array_merge function, like it:
array_merge($a1,$a2)
I created an example for your code here:
http://codepad.org/zzkndGQZ
You can use the email like array's key, to end, use array_combine to have indeces since 0 to N number...
<?php
$oldArray = array(
array(
'email' => 'test#gmail.com',
'meta' => array(
'product' => array(
'id' => '1',
'content' => 'This is content'
)
)
), array(
'email' => 'test2#gmail.com',
'meta' => array(
'product' => array(
'id' => '2',
'content' => 'This is content'
)
)
), array(
'email' => 'test2#gmail.com',
'meta' => array(
'product' => array(
'id' => '3',
'content' => 'This is content'
)
)
)
);
$newArray = array();
foreach($oldArray as $element){
if(isset($newArray[$element['email']])) {
if(!isset($newArray[$element['email']]['meta']['product'][0]))
$newArray[$element['email']]['meta']['product'] = array($newArray[$element['email']]['meta']['product']);
$newArray[$element['email']]['meta']['product'][] = $element['meta']['product'];
} else {
$newArray[$element['email']] = $element;
}
}
//For index since 0 to n
print_r(array_combine(range(0,count($newArray)-1), $newArray));
You can build a new array with the sorted informations.
It should be working with something like this, but not tested:
$sorted_array = [];
$i = 0;
foreach ($unsorted_array as $array) {
if(!empty($array['email']) && !empty($array['meta']['product'])){
$sorted_array[$i]['email'] = $array['email'];
$sorted_array[$i]['meta']['product'][] = $array['meta']['product'];
$i++;
}
}
print_r($sorted_array);

Change Array Value

I have simple questions, How to change text if id = cur_three from array below ?
$arr = array(
'id' => 'curr',
'lists' => array(
array(
'id' => 'cur_one',
'text' => 'Dollar',
),
array(
'id' => 'cur_two',
'text' => 'Euro',
),
array(
'id' => 'cur_three',
'text' => 'Peso',
),
)
);
Thank you very much...
Something simple:
foreach($arr['lists'] as $subArr) {
if ($subArr['id'] == 'cur_three') {
$subArr['text'] = 'not Peso';
}
}
Sure. Like this:
foreach($arr['lists'] as $key => $child) {
if($child['id'] == 'cur_three') {
$arr['lists'][$key]['text'] = "INR";
}
}

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

Categories