String to variable-depth multidimensional array - php

I'm trying to create a multidimensional array from a string (received from $_GET, input is validated, but not in this example). Each '-' will indicate a level in the multidimensional array.
Values can look like this (any form really, as long as '-' is present between keys). The array of values can map to any depth in the multidimensional array.
$array = array(
'page-title' => 'Title of a page',
'page-url' => 'http://www.mypage.com',
'meta-page-author' => 'Some guy',
'meta-page-created' => 'some timestamp'
);
I've tried different solutions, but the only thing working until now is the inital loop and extract of keys.
foreach ($array as $key => $value) {
if (strpos($key, '-') !== false) {
$keyArray = explode('-', $key);
// ??
}
}
The output I'm hoping for, should look like this:
array(
'page' => array(
'title' => 'Title of a page',
'url' => 'http://www.mypage.com'
),
'meta' => array(
'page' => array(
'author' => 'Some guy',
'created' => 'some timestamp'
)
)
);

Something like this should work:
<?php
$array = array(
'page-title' => 'Title of a page',
'page-url' => 'http://www.mypage.com',
'meta-page-author' => 'Some guy',
'meta-page-created' => 'some timestamp'
);
$result = array();
foreach ($array as $key => $value) {
$keys = strpos($key, '-') !== false ? explode('-', $key) : array($key);
$ptr = &$result;
foreach ($keys as $k) {
if (!isset($ptr[$k])) {
$ptr[$k] = array();
}
$ptr = &$ptr[$k];
}
if (empty($ptr)) {
$ptr = $value;
} else {
$ptr[] = $value;
}
}
print_r($result);
What I did was explode your keys just like you were doing. I then looped through them creating a new array if the array didn't already exist. Using a reference I save the current point I was at in the array. Then once I had hit the last key I assigned the value. Hope this helps.
EDIT: Based on cHao's recommendation I changed
$keys = strpos($key, '-') !== false ? explode('-', $key) : $key;
to
$keys = strpos($key, '-') !== false ? explode('-', $key) : array($key);
to prevent failure on the foreach.
EDIT 2: I changed
$ptr = $value;
to
if (empty($ptr)) {
$ptr = $value;
} else {
$ptr[] = $value;
}
to handle cases like:
$array = array(
'page-title' => 'Title of a page',
'page-url' => 'http://www.mypage.com',
'meta-page-author' => 'Some guy',
'meta-page-created' => 'some timestamp',
'page' => 'foo'
);

Just so you're aware, PHP can be made to accept whole big arrays like that. If you name the form elements like 'somename[page][title]', then when the form returns, you should see them already arranged as an array in $_GET.
In case you have your heart set on the current naming scheme, though...
$result = array();
foreach ($array as $key => $value) {
$current =& $result;
if (strpos($key, '-') !== false) {
$keyArray = explode('-', $key);
$bottomKey = array_pop($keyArray);
foreach ($keyArray as $subKey) {
if (!isset($current[$subKey]))
$current[$subKey] = array();
$current =& $current[$subKey];
}
} else {
$bottomKey = $key;
}
$current[$bottomKey] = $value;
}

<?php
$array = array(
'page-title' => 'Title of a page',
'page-url' => 'http://www.mypage.com',
'meta-page-author' => 'Some guy',
'meta-page-created' => 'some timestamp'
);
$result = array();
foreach ($array as $key => $value) {
if (strpos($key, '-') !== false) {
$ak = "result['" . str_replace('-', '\'][\'', $key) . "'] = \"".$value."\"";
eval('$'.$ak.';');
}
}
var_dump($result);
?>
hope that helps

Related

Remove array key prefix recursively

I have an array in the following format (each child array has parent's key as a prefix):
$input = array(
'seo_text' => array(
'seo_text_title' => '',
'seo_text_description' => '',
'seo_text_button' => array(
'seo_text_button_text' => '',
'seo_text_button_url' => '',
'seo_text_button_new_tab_enabled' => '',
),
),
);
I want to convert it into the following format:
$input = array(
'seo_text' => array(
'title' => '',
'description' => '',
'button' => array(
'text' => '',
'url' => '',
'new_tab_enabled' => '',
),
),
);
I'm trying to write a recursive function, but it's not working the way it's suppose to be working.
A solution:
function removeKeyPrefix(array $array, string $prefix = ''): array
{
$newArray = [];
$prefixLength = strlen($prefix);
foreach ($array as $key => $value) {
if (substr($key, 0, $prefixLength) === $prefix) {
$newKey = substr($key, $prefixLength);
} else {
$newKey = $key;
}
$newArray[$newKey] = is_array($value) ? removeKeyPrefix($value, $key.'_') : $value;
}
return $newArray;
}
$input = removeKeyPrefix($input);
Online demo on 3v4l
function sanitizeKeys(array $items, $previousKey = '') : array
{
$previousKey .= "_";
return array_reduce(
array_keys($items),
function($result, $key) use ($previousKey, $items) {
$newKey = (0 === strpos($key, $previousKey)) ? substr_replace($key, '', 0, strlen($previousKey)) : $key;
$result[$newKey] = is_array($items[$key]) ? sanitizeKeys($items[$key], $key) : $items[$key];
return $result;
}, []
);
};
sanitizeKeys($input);
Make a function to change last occurrence in the slug
function fun($array,$slug){
foreach($array as $key=>$val){
$last = end(explode($slug,$key));
if(is_array($val)){
$val = fun($val,$key."_");
}
$array[$last] = $val;
unset($array[$key]);
}
return $array;
}
$input["seo_text"] = fun($input["seo_text"],"seo_text_");
print_r($input);
Live demo : https://eval.in/934155
Output is
Array
(
[seo_text] => Array
(
[title] =>
[description] =>
[button] => Array
(
[text] =>
[url] =>
[new_tab_enabled] =>
)
)
)
For if you have many element in second level use foreach to call function like below
foreach($input as $key=>$val){
$input[$key] = fun($val,$key."_");
}
print_r($input);
https://eval.in/934156

PHP: Convert Multidimensional Array to String

I am trying to convert a multidimensional array into a string.
Till now I have been able to convert a pipe delimited string into an array.
Such as:
group|key|value
group|key_second|value
Will render into the following array:
$x = array(
'group' => array(
'key' => 'value',
'key_second' => 'value'
),
);
However, now I want it to be the other way around, where a multidimensional array is provided and I want to convert it to a pipe delimited string just like in the first code example.
Any ideas how to do this ?
PS: Please do note that the array can dynamically have any depth.
For example:
$x['group']['sub_group']['category']['key'] = 'value'
Translates to
group|sub_group|category|key|value
I have created my own function:
This should have no problem handling even big arrays
function array_to_pipe($array, $delimeter = '|', $parents = array(), $recursive = false)
{
$result = '';
foreach ($array as $key => $value) {
$group = $parents;
array_push($group, $key);
// check if value is an array
if (is_array($value)) {
if ($merge = array_to_pipe($value, $delimeter, $group, true)) {
$result = $result . $merge;
}
continue;
}
// check if parent is defined
if (!empty($parents)) {
$result = $result . PHP_EOL . implode($delimeter, $group) . $delimeter . $value;
continue;
}
$result = $result . PHP_EOL . $key . $delimeter . $value;
}
// somehow the function outputs a new line at the beginning, we fix that
// by removing the first new line character
if (!$recursive) {
$result = substr($result, 1);
}
return $result;
}
Demo provided here http://ideone.com/j6nThF
You can also do this using a loop like this:
$x = array(
'group' => array(
'key' => 'value',
'key_second' => 'value'
)
);
$yourstring ="";
foreach ($x as $key => $value)
{
foreach ($x[$key] as $key2 => $value2)
{
$yourstring .= $key.'|'.$key2.'|'.$x[$key][$key2]."<BR />";
}
}
echo $yourstring;
Here is a working DEMO
This code should do the thing.
You needed a recursive function to do this. But be careful not to pass object or a huge array into it, as this method is very memory consuming.
function reconvert($array,$del,$path=array()){
$string="";
foreach($array as $key=>$val){
if(is_string($val) || is_numeric($val)){
$string.=implode($del,$path).$del.$key.$del.$val."\n";
} else if(is_bool($val)){
$string.=implode($del,$path).$del.$key.$del.($val?"True":"False")."\n";
} else if(is_null($val)){
$string.=implode($del,$path).$del.$key.$del."NULL\n";
}else if(is_array($val)=='array') {
$path[]=$key;
$string.=reconvert($val,$del,$path);
array_pop($path);
} else {
throw new Exception($key." has type ".gettype($val).' which is not a printable value.');
}
}
return $string;
}
DEMO: http://ideone.com/89yLLo
You can do it by
Look at serialize and unserialize.
Look at json_encode and json_decode
Look at implode
And Possible duplicate of Multidimensional Array to String
You can do this if you specifically want a string :
$x = array(
'group' => array(
'key' => 'value',
'key_second' => 'value'
),
'group2' => array(
'key2' => 'value',
'key_second2' => 'value'
),
);
$str='';
foreach ($x as $key=>$value)
{
if($str=='')
$str.=$key;
else
$str.="|$key";
foreach ($value as $key1=>$value1)
$str.="|$key1|$value1";
}
echo $str; //it will print group|key|value|key_second|value|group2|key2|value|key_second2|value

Combine repeating elements as array in a multidimensional array

I was wondering when working with multimedional arrays, if a certain key is the same, is there a way to combine the contents of other keys into its own array if a certain key is the same?
Something like this:
// name is the same in both arrays
array(
array(
'name' => 'Pepsi',
'store' => 'Over here',
'number' => '1234567'
),
array(
'name' => 'Pepsi',
'store' => 'Over here',
'number' => '5556734'
)
)
into something like this
array(
array(
'name' => 'Pepsi',
'store' => array('Over here', 'Over here'),
'number' => array('1234567', '5556734')
)
)
The defining key is checking if the name element is the same for the other arrays.
You can try a function like this.
function mergeByKey($array,$key){
$tmp_array = array();
foreach ( $array as $k => $row ) {
$merged = false;
foreach ($tmp_array as $k2 => $tmp_row){
if ($row[$key] == $tmp_row[$key]){
foreach ( $row as $k3 => $value ) {
if ($k3 == $key) continue;
$tmp_array[$k2][$k3][] = $value;
$merged = true;
}
}
if ($merged) break;
}
if (!$merged) {
$new_row = array();
foreach ( $row as $k4 => $value ) {
if ($k4 == $key) $new_row[$k4] = $value;
else $new_row[$k4] = array($value);
}
$tmp_array[] = $new_row;
}
}
foreach ( $tmp_array as $t => $row ) {
foreach ( $row as $t2 => $value ) {
if ( count($value) == 1 && $t2 != $key ) $tmp_array[$t][$t2] = $value[0];
}
}
return $tmp_array;
}
passing the array as first parameter and the key as second one.
I'm referencing to your array structure
edited: missed a piece
edited2: if resultin array contains elements with one string, it returns a string and not a array with one element
demo
This function uses a given field name as the grouping identifier and turns all other fields into arrays.
Note that single occurrences of your field name will yield arrays with a single element for the other fields. I wasn't sure whether that's a desirable trait, but just making sure you know ;-)
$arr = array(
array(
'name' => 'Pepsi',
'store' => 'Over here',
'number' => '1234567'
),
array(
'name' => 'Pepsi',
'store' => 'Over here',
'number' => '5556734'
)
);
function mergeArray($array, $column)
{
$res = array();
foreach ($array as $item) {
foreach ($item as $key => $value) {
if ($key === $column) {
$res[$column][$key] = $value;
} else {
$res[$column][$key][] = $value;
}
}
}
return array_values($res);
}
print_r(mergeArray($arr, 'name'));
Demo
Thanks to Gianni Lovece for her answer but I was able to develop a much simpler solution based on this problem. Just plug in the $result_arr to browse through and the $key you want to use as basis and it immediately outputs a multidimensional array with non-repeating values for repeating elements (see example below).
function multiarray_merge($result_arr, $key){
foreach($result_arr as $val){
$item = $val[$key];
foreach($val as $k=>$v){
$arr[$item][$k][] = $v;
}
}
// Combine unique entries into a single array
// and non-unique entries into a single element
foreach($arr as $key=>$val){
foreach($val as $k=>$v){
$field = array_unique($v);
if(count($field) == 1){
$field = array_values($field);
$field = $field[0];
$arr[$key][$k] = $field;
} else {
$arr[$key][$k] = $field;
}
}
}
return $arr;
}
For example, in the sample array for this question, running multiarray_merge($mysample, 'name') returns
array(
'Pepsi' => array(
'name' => 'Pepsi',
'store' => 'Over here', // String: Not an array since values are not unique
'number' => array('1234567', '5556734') // Array: Saved as array since values are unique
)
);

Invoking Multidimensional Associative Arrays in Templates

I'm building a small template system and i'm looking for a way to invoke multidimensional associative arrays using dots. For example:
$animals = array(
'four-legged' => array (
'cute' => 'no',
'ugly' => 'no',
'smart' => array('best' => 'dog','worst' => 'willy')
),
'123' => '456',
'abc' => 'def'
);
Then, in my template, if I wanted to show 'dog', I would put:
{a.four-legged.smart.best}
Well, given a string with four-legged.smart.worst:
function getElementFromPath(array $array, $path) {
$parts = explode('.', $path);
$tmp = $array;
foreach ($parts as $part) {
if (!isset($tmp[$part])) {
return ''; //Path is invalid
} else {
$tmp = $tmp[$part];
}
}
return $tmp; //If we reached this far, $tmp has the result of the path
}
So you can call:
$foo = getElementFromPath($array, 'four-legged.smart.worst');
echo $foo; // willy
And if you want to write elements, it's not much harder (you just need to use references, and a few checks to default the values if the path doesn't exist)...:
function setElementFromPath(array &$array, $path, $value) {
$parts = explode('.', $path);
$tmp =& $array;
foreach ($parts as $part) {
if (!isset($tmp[$part]) || !is_array($tmp[$part])) {
$tmp[$part] = array();
}
$tmp =& $tmp[$part];
}
$tmp = $value;
}
Edit: Since this is in a template system, it may be worth while "compiling" the array down to a single dimension once, rather than traversing it each time (for performance reasons)...
function compileWithDots(array $array) {
$newArray = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$tmpArray = compileWithDots($value);
foreach ($tmpArray as $tmpKey => $tmpValue) {
$newArray[$key . '.' . $tmpKey] = $tmpValue;
}
} else {
$newArray[$key] = $value;
}
}
return $newArray;
}
So that would convert:
$animals = array(
'four-legged' => array (
'cute' => 'no',
'ugly' => 'no',
'smart' => array(
'best' => 'dog',
'worst' => 'willy'
)
),
'123' => '456',
'abc' => 'def'
);
Into
array(
'four-legged.cute' => 'no',
'four-legged.ugly' => 'no',
'four-legged.smart.best' => 'dog',
'four-legged.smart.worst' => 'willy',
'123' => '456',
'abc' => 'def',
);
Then your lookup just becomes $value = isset($compiledArray[$path]) ? $compiledArray[$path] : ''; instead of $value = getElementFromPath($array, $path);
It trades pre-computing for inline speed (speed within the loop)...

PHP rename array keys in multidimensional array

In an array such as the one below, how could I rename "fee_id" to "id"?
Array
(
[0] => Array
(
[fee_id] => 15
[fee_amount] => 308.5
[year] => 2009
)
[1] => Array
(
[fee_id] => 14
[fee_amount] => 308.5
[year] => 2009
)
)
foreach ( $array as $k=>$v )
{
$array[$k] ['id'] = $array[$k] ['fee_id'];
unset($array[$k]['fee_id']);
}
This should work
You could use array_map() to do it.
$myarray = array_map(function($tag) {
return array(
'id' => $tag['fee_id'],
'fee_amount' => $tag['fee_amount'],
'year' => $tag['year']
); }, $myarray);
$arrayNum = count($theArray);
for( $i = 0 ; $i < $arrayNum ; $i++ )
{
$fee_id_value = $theArray[$i]['fee_id'];
unset($theArray[$i]['fee_id']);
$theArray[$i]['id'] = $fee_id_value;
}
This should work.
Copy the current 'fee_id' value to a new key named 'id' and unset the previous key?
foreach ($array as $arr)
{
$arr['id'] = $arr['fee_id'];
unset($arr['fee_id']);
}
There is no function builtin doing such thin afaik.
This is the working solution, i tested it.
foreach ($myArray as &$arr) {
$arr['id'] = $arr['fee_id'];
unset($arr['fee_id']);
}
The snippet below will rename an associative array key while preserving order (sometimes... we must). You can substitute the new key's $value if you need to wholly replace an item.
$old_key = "key_to_replace";
$new_key = "my_new_key";
$intermediate_array = array();
while (list($key, $value) = each($original_array)) {
if ($key == $old_key) {
$intermediate_array[$new_key] = $value;
}
else {
$intermediate_array[$key] = $value;
}
}
$original_array = $intermediate_array;
Converted 0->feild0, 1->field1,2->field2....
This is just one example in which i get comma separated value in string and convert it into multidimensional array and then using foreach loop i changed key value of array
<?php
$str = "abc,def,ghi,jkl,mno,pqr,stu
abc,def,ghi,jkl,mno,pqr,stu
abc,def,ghi,jkl,mno,pqr,stu
abc,def,ghi,jkl,mno,pqr,stu;
echo '<pre>';
$arr1 = explode("\n", $str); // this will create multidimensional array from upper string
//print_r($arr1);
foreach ($arr1 as $key => $value) {
$arr2[] = explode(",", $value);
foreach ($arr2 as $key1 => $value1) {
$i =0;
foreach ($value1 as $key2 => $value2) {
$key3 = 'field'.$i;
$i++;
$value1[$key3] = $value2;
unset($value1[$key2]);
}
}
$arr3[] = $value1;
}
print_r($arr3);
?>
I wrote a function to do it using objects or arrays (single or multidimensional) see at https://github.com/joaorito/php_RenameKeys.
Bellow is a simple example, you can use a json feature combine with replace to do it.
// Your original array (single or multi)
$original = array(
'DataHora' => date('YmdHis'),
'Produto' => 'Produto 1',
'Preco' => 10.00,
'Quant' => 2);
// Your map of key to change
$map = array(
'DataHora' => 'Date',
'Produto' => 'Product',
'Preco' => 'Price',
'Quant' => 'Amount');
$temp_array = json_encode($original);
foreach ($map AS $k=>$v) {
$temp_array = str_ireplace('"'.$k.'":','"'.$v.'":', $temp);
}
$new_array = json_decode($temp, $array);
Multidimentional array key can be changed dynamically by following function:
function change_key(array $arr, $keySetOrCallBack = [])
{
$newArr = [];
foreach ($arr as $k => $v) {
if (is_callable($keySetOrCallBack)) {
$key = call_user_func_array($keySetOrCallBack, [$k, $v]);
} else {
$key = $keySetOrCallBack[$k] ?? $k;
}
$newArr[$key] = is_array($v) ? array_change_key($v, $keySetOrCallBack) : $v;
}
return $newArr;
}
Sample Example:
$sampleArray = [
'hello' => 'world',
'nested' => ['hello' => 'John']
];
//Change by difined key set
$outputArray = change_key($sampleArray, ['hello' => 'hi']);
//Output Array: ['hi' => 'world', 'nested' => ['hi' => 'John']];
//Change by callback
$outputArray = change_key($sampleArray, function($key, $value) {
return ucwords(key);
});
//Output Array: ['Hello' => 'world', 'Nested' => ['Hello' => 'John']];
I have been trying to solve this issue for a couple hours using recursive functions, but finally I realized that we don't need recursion at all. Below is my approach.
$search = array('key1','key2','key3');
$replace = array('newkey1','newkey2','newkey3');
$resArray = str_replace($search,$replace,json_encode($array));
$res = json_decode($resArray);
On this way we can avoid loop and recursion.
Hope It helps.

Categories