PHP: Convert Multidimensional Array to String - php

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

Related

Search differences in array numeric of associative arrays by key of associative arrays

I am trying to find a way to extract the changes between two arrays with the same structure.
Both are arrays of subarray associatives
The associative pieces always have a key that is the key to finding
the differences
The differences can be modifications to a subarray, a new subarray or
the elimination of a subarray
Before the comparison the array was ordered using as a value to order, the key 'name'
Data contains all scenarios used in testing
private function getValuesItem(array $array): string
{
$string = '';
foreach ($array as $clave => $valor) {
$string .= $clave . ': ' . $this->setTypeToString($valor) .'<br /> ';
}
return $string;
}
$oldjson = "[{"name":"column1","type":"boolean","length":255},{"name":"column2","type":"integer","length":10},{"name":"column3","type":"boolean","length":100},{"name":"column4","type":"integer","length":255},{"name":"column5","type":"string","length":100}]";
$newjson = "[{"name":"column1","type":"boolean","length":255},{"name":"column2","type":"integer","length":10},{"name":"column3","type":"boolean","length":100},{"name":"column4","type":"integer","length":255},{"name":"column5","type":"string","length":100}]"
$differences = array();
$newer = json_decode($newjson, true);
$older = json_decode($oldjson, true);
foreach ($newer as $new) {
foreach ($older as $old) {
// Modifications
if ($new['name'] === $old['name']) {
foreach ($new as $clave => $valor) {
if ($old[$clave] != $new[$clave]) {
$differences[] = [
'type' => 'Modified',
'name' => $new['name'],
'old' => $this->setTypeToString($old[$clave]),
'new' => $this->setTypeToString($new[$clave])
];
}
}
continue;
} else {
// Added
if ($new['name'] !== $old['name']) {
dd($new['name'],$old['name']);
$differences[] = [
'type' => 'Added',
'name' => $new['name'],
'old' => '',
'new' => $this->getValuesItem($new)
];
break 2;
}
}
}
}
But I got stuck and I am unable to do it. When I create new unit tests, adding or modifying some of the possibilities something goes wrong.

return all keys of nested array

given a nested array of arbitrary depth like this:
$array = array(
1400=>
array(7300=>
array(
7301=> array(),
7302=> array(),
7305=> array(
7306=>array()
),
),
7314=>array()
),
);
how would one get the hierarchy of keys for any key.
for example:
getkeys(7305);
should return 1400,7300,7305 in that order
or
getkeys(7314);
should return 1400,7314
all array keys are unique values
Using RecursiveIteratorIterator
$array = array(
1400 => array(
7300 => array(
7301=> array(),
7302 => array(),
7305 => array(
7306=>array()
),
),
7314=>array()
),
);
function getKeys($key, $array) {
$found_path = [];
$ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($array), RecursiveIteratorIterator::SELF_FIRST);
foreach ($ritit as $leafValue) {
$path = array();
foreach (range(0, $ritit->getDepth()) as $depth) {
$path[] = $ritit->getSubIterator($depth)->key();
}
if (end($path) == $key) {
$found_path = $path;
break;
}
}
return $found_path;
}
print_r(getKeys(7305, $array));
// Array
// (
// [0] => 1400
// [1] => 7300
// [2] => 7305
// )
This is very interesting problem you have so I tried to make a function that will echo your keys. If this is not good enough pls let me know I can improve code. Thanks.
<?php
$a = array(
1400=>
array(7300=>
array(
7301=> array(),
7302=> array(),
7305=> array(
7306=>array()
),
),
7314=>array()
),
);
$mykey = 7306;
$level = 0;
$result = array();
$resultarray = test($a,$mykey,$level,$result);
function test($array,$mykey,$level,$result){
$level++;
foreach($array as $key => $element){
if($key == $mykey){
echo 'found';
print_r($result);
exit;
} else if(is_array($element)){
$result[$level] = $key;
$result1 = test($element,$mykey,$level,$result);
}
}
}
The idea is to check current array branch, and if the needle key isn't found, then iterate current items and check their array child nodes by recursive function calls. Before each step down we push a current key to stack, and pop the stack if the function does not found a needle key in whole branch. So if the key found, the function returns true by the chain, preserving successful keys in the stack.
function branchTraversing(& $branch, & $key_stack, $needle_key) {
$found = false;
if (!array_key_exists($needle_key, $branch)) {
reset($branch);
while (!$found && (list($key, $next_branch) = each($branch))) {
if (is_array($next_branch)) {
array_push($key_stack, $key);
$found = branchTraversing($next_branch, $key_stack, $needle_key);
if (!$found) {
array_pop($key_stack);
}
}
}
} else {
array_push($key_stack, $needle_key);
$found = true;
}
return $found;
}
function getPath(& $array, $needle_key) {
$path = [];
branchTraversing($array, $path, $needle_key);
return $path;
}
$test_keys = [1400, 7300, 7302, 7306, 7314, 666];
foreach ($test_keys as $search_key) {
echo '<p>' . $search_key . ' => [ '
. implode(', ', getPath($array, $search_key)) . ' ]</p>';
}

Outputting Array Keys of Multi-Dimensional Array

I'm trying to write a function that takes a multi-dimensional array as input and outputs a multi-line string of keys like the following
['key']['subkey']
['key']['another_subkey']
['key']['another_subkey']['subkey_under_subkey']
['key']['yet_another_subkey']
['another_key']['etc']
Here is my attempt. It has problems when you get to the second level.
function get_array_keys_as_string($array){
$output = "";
foreach($array as $k => $v){
if(is_array($v)){
$string = get_array_keys_as_string($v);
$prepend = "['$k']";
$string = $prepend.str_replace("\n","\n".$prepend, $string);
$output .= $string;
}
else{
$output .= "['$k']\n";
}
}
return $output;
}
I know I need a recursive function, but so far my attempts have come up short.
To get the exact output you asked for use the following:
$arr = array(
"key" => array(
"subkey" => 1,
"another_subkey" => array(
"subkey_under_subkey" => 1
),
"yet_another_subkey" => 1
),
"another_key" => array(
"etc" => 1
)
);
function print_keys_recursive($array, $path = false) {
foreach($array as $key=>$value) {
if(!is_array($value)) {
echo $path."[".$key."]<br/>";
} else {
if($path) {
echo $path."[".$key."]<br/>";
}
print_keys_recursive($value, $path."[".$key."]");
}
}
return;
}
print_keys_recursive($arr);
Output:
[key][subkey]
[key][another_subkey]
[key][another_subkey][subkey_under_subkey]
[key][yet_another_subkey]
[another_key][etc]
Not sure how you want the output since you have not provided an example array, just the result, but here is an example based on the following array,
$array = array(
"key" => array(
"subkey" => 1,
"another_subkey" => array("2", "subkey_under_subkey" => 3),
"yet_another_subkey" => 4
),
"another_key" => array("etc"),
"last_key" => 0
);
Using the following function,
function recursive_keys($arr, $history = NULL)
{
foreach ($arr as $key => $value)
{
if (is_array($value))
recursive_keys($value, $history."['".$key."']");
else
echo $history."['".$key."']\n";
}
}
Output of recursive_keys($array) is,
['key']['subkey']
['key']['another_subkey']['0']
['key']['another_subkey']['subkey_under_subkey']
['key']['yet_another_subkey']
['another_key']['0']
['last_key']
Try this
function arrayMultiKeys($array,$depth = 0){
foreach($array as $k=>$v){
echo "['".$k."']";
if(is_array($v)){
arrayMultiKeys($v,$depth + 1);
}
if($depth == 0 ){
echo "<br>";
}
}
}

Recursively chain specific values together without passing an array by reference

Imagine the following multi-dimensional array:
$a = array(
'key' => 'hello',
'children' => array(
array(
'key' => 'sub-1'
),
array(
'key' => 'sub-2',
'children' => array(
array(
'key' => 'sub-sub-1'
)
)
)
)
);
I require a function that recursively runs through such an array and then finally returns a chain of all the values of a certain sub-key, using a glue string.
function collectKeyChain(array $array, $key, $parentKey, $glue){
foreach($array as $k => $v){
if(is_array($v[$parentKey]))
$children=self::collectKeyChain($v[$parentKey], $key, $parentKey, $glue, $out);
$chain[]=$glue . implode($glue, $children);
}
return $chain;
}
Called this way:
collectValueChain($a, 'key', 'children', '/');
Should then return this:
array(
'hello',
'hello/sub-1',
'hello/sub-2',
'hello/sub-2/sub-sub-1'
)
Unfortunately my brain seems completely unable to perform the task of "nested thinking". The code provided in the function above doesn't work, simply because it makes no sense. I can either use the recursive function to return an array or a string. But in the final output i require an array. On the other hand i need to chain the elements together.
That's the dilemma. And the only solution that came up in my head was using another parameter, that is passed by reference, which is an array that is being filled with the results.
Like this:
collectValueChain($a, 'key', 'children', '/', $arrayToBeFilledWithResults);
But i was unable to make even this work without getting into using multiple functions.
Perhaps it just cannot be done more easily, but i would still like to find out.
Try this one:
function collectKeyChain(array $array, $key, $parentKey, $glue) {
$return = array();
foreach ($array as $k => $v) {
if ($k == $key) {
$base = $v;
$return[] = $base;
} elseif ($k == $parentKey && is_array($v)) {
foreach ($v as $_v) {
$children = collectKeyChain($_v, $key, $parentKey, $glue);
foreach ($children as $child) {
$return[] = $base . $glue . $child;
}
}
}
}
return $return;
}
Note that if this is to be a static method in a class you have to add self:: to the recursive method call.
A more simple version, without lots of foreach. Consider the second approach:
collectValueChain($a, 'key', 'children', '/', $arrayToBeFilledWithResults);
I do this:
function collectValueChain($a, $keyname, $parent, $glue, &$rtn, $pre="") {
$_pre = "";
if ($a[$keyname]) {
$rtn[] = $_pre = $pre.$glue.$a[$keyname];
}
if ($a[$parent]) {
if(is_array($a[$parent])) {
foreach($a[$parent] as $c)
collectValueChain($c, $keyname, $parent, $glue, $rtn, $_pre );
} else {
collectValueChain(a[$parent], $keyname, $parent, $glue, $rtn, $_pre );
}
}
$qtd = count($rtn);
return $rtn[-1];
}

String to variable-depth multidimensional array

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

Categories