Dynamic Array traversal in PHP - php

I want to build a hierarchy from a one-dimensional array and can (almost) do so with a more or less hardcoded code. How can I make the code dynamic?
Perhaps with while(isset($array[$key])) { ... }?
Or, with an extra function? Like this: $out = my_extra_traverse_function($array,$key);
function array_traverse($array,$key=NULL) {
$out = (string) $key;
$out = $array[$key] . "/" . $out;
$key = $array[$key];
$out = $array[$key] ? $array[$key] . "/" . $out : "";
$key = $array[$key];
$out = $array[$key] ? $array[$key] . "/" . $out : "";
$key = $array[$key];
$out = $array[$key] ? $array[$key] . "/" . $out : "";
return $out;
}
$a = Array(102=>101, 103=>102, 105=>107, 109=>105, 111=>109, 104=>111);
echo array_traverse($a,104);
Output: 107/105/109/111/104

I THINK you want:
function array_traverse($array, $key = null) {
$out = (string) $key;
if (isset($array[$key])) {
$out = array_traverse($array, $array[$key]) . '/' . $out;
}
return $out;
}
Or, for a non-recursive method:
function array_traverse($array, $key = null) {
$out = (string) $key;
while(isset($array[$key])) {
$out = $array[$key] . '/' . $out;
$key = $array[$key];
}
return $out;
}

Yes, I would say isset() is the way to go:
traverse($array, $value) {
$result = array();
while (isset($array[$value])) {
array_unshift($result, $value);
# or just $result[] = $value if you want to append
# instead of prepending
$value = $array[$value];
}
return $result;
# or return implode('/', traverse(array(...))),
# but I always prefer array return types in such cases:
# they are much more flexible to the users of the function
}
# BTW: Using implode will avoid having an unnecessary
# leading/trailing delimiter (slash in this case)
echo implode('/', traverse(array(...)));

Related

How to convert $x['a']['b']['c'] = d to $x['a.b.c'] = d easily

I have a multiple level array field need to be submit via a form. Not sure what will be the easiest solution:
As php will auto convert dot to underscore, so i change it to array:
$form['a.b_b.c'] ==> <input name="a[b_b][c]" value="$form[a.b_b.c]">
And I got $_POST[a][b_b][c] correctly.
But what is the easiest way to assign this $_POST value back to the original $form['a.b_b.c'] without much loops? eg
$form['a.b_b.c'] = $_POST['a']['b_b']['c'];
Or is there a better way to walk around?
very close to this question but still not yet:
Convert dot syntax like "this.that.other" to multi-dimensional array in PHP
And here is my current solution:
foreach ($form as $k) {
$form[$k] = assign_val($_POST, $k);
}
function assign_val($arr = [], $key = '') {
$keys = explode('.', $key);
$val = &$arr;
foreach ($keys as $k) {
$val = &$val[$k];
}
return $val;
}
I would have a function flatten and do something like:
function flatten($array, $prefix = '') {
$result = array();
foreach($array as $key=>$value) {
if(is_array($value)) {
$result = $result + flatten($value, $prefix . $key . '.');
}
else {
$result[$prefix . $key] = $value;
}
}
return $result;
}
And then you can do:
$form = flatten($_POST);

Convert multidimensional array keys to string

I have a multidimensional array like this:
$array1['first']='myvalue1';
$array1['second']=array();
$array1['second']['first']='myvalue21';
$array1['second']['second']='myvalue22';
$array1['second']['third']=array();
$array1['second']['third']['first']='myvalue231';
$array1['second']['fourth']='myvalue24';
$array1['third']='myvalue3';
And another array like:
$array2['second-first']='newvalue21';
$array2['second-third-first']='newvalue231';
And I can't get the way to walk $array1 recursively to check, in each iteration, if exist any element in $array2 with a key equivalent to the current element key and their parents converted to string.
To simplify the question, I will have enough with a function that prints something like:
// walking $array1:
first
second-first
second-second
second-third-first
second-fourth
third
Thank you.
Solution based on Clément Malet answer
function print_array_reccur ($array1, $array2, $str = '')
{
foreach ($array1 as $key => $val) {
if (is_array($val)) {
if ($str == '') {
print_array_reccur($val, $array2, $key);
} else {
print_array_reccur($val, $array2, $str . '-' . $key);
}
} else {
if ($str == '') {
$result = $key;
} else {
$result = $str . '-' . $key;
}
if(isset($array2[$result]))
{
echo 'Found $array2['.$result.'] = ' . $array2[$result] . "\n";
}
}
}
}
print_array_reccur ($array1, $array2);
/* OUTPUT:
Found $array2[second-first] = newvalue21
Found $array2[second-third-first] = newvalue231
*/
I really didn't understand what you wanted in the very end, and what you want to achieve later on with your second array.
But since you are looking for a way to print something (glad you simplified that way), here it is :
$array1['first']='myvalue1';
$array1['second']=array();
$array1['second']['first']='myvalue21';
$array1['second']['second']='myvalue22';
$array1['second']['third']=array();
$array1['second']['third']['first']='myvalue231';
$array1['second']['fourth']='myvalue24';
$array1['third']='myvalue3';
function print_array_reccur ($array, $str = '') {
foreach ($array as $key => $val) {
if (is_array($val)) {
if ($str == '') {
print_array_reccur($val, $key);
} else {
print_array_reccur($val, $str . '-' . $key);
}
} else {
if ($str == '') {
echo $key . "\n";
} else {
echo $str . '-' . $key . "\n";
}
}
}
}
print_array_reccur ($array1);
Output :
first
second-first
second-second
second-third-first
second-fourth
third

Add string at the end of every loop but not for the last loop

I am creating a function to set query vars as below.
function set_query_var(array $params)
{
$count = count($params);
$query_var = '?';
foreach ( $params as $key => $value)
{
$query_var .= $key . '=' . $value .
(($count > 1) ? '&' : NULL);
}
return $query_var;
}
Above function works fine but only issue is that, it is adding & to the end loop which I don't want.
How can I fix it?
I always use implode and array:
function set_query_var(array $params)
{
$query_var = [];
foreach ($params as $key => $value) {
$query_var[] = "{$key}={$value}";
}
return '?' . implode('&', $query_var);
}
function set_query_var(array $params)
{
$count = count($params);
$query_var = '?';
foreach ( $params as $key => $value)
{
$query_var .= $key . '=' . $value .
(($count > 1) ? '&' : NULL);
}
$query_var = rtrim( $query_var, "&");
return $query_var;
}
An easy and dirty solution to you problem is to remove the last & from your string.
rtrim($string, "&");
would cut trailing &.

Is it possible for a variable to evaluate to a multi-dimensional PHP array index

Given a multidimensional array or dictionary $array.
And assuming that $array['foo']['bar']['baz'] = 'something';
Is there a way other than via an eval statement for me use the multi-dimentional index foo/bar/baz? (The use case is in creating the index dynamically i.e. The function does not know what /foo/bar/baz/ is).
The only way I could figure to do this was:
$item = testGetIndex($array, "'foo']['bar']['baz'");
function testGetIndex($array, $index) {
eval('$item = $array[' . $index . '];');
return $item;
}
Note:
I should mention that I do not want to search this array. This is a weird use case. I am being passed a very large multi dimensional array and it's ugly to have to use constructs like..
$array[foo][bar]..[baz] to make modifications to the array.
Blatently reusing my answer here:
function recurseKeys(array $keys,array $array){
$key = array_shift($keys);
if(!isset($array[$key])) return null;
return empty($keys) ?
$array[$key]:
recurseKeys($keys,$array[$key];
}
$values = recurseKeys(explode('/','foo/bar/baz'),$yourArray);
edit: as Jack pointed out, recursion is not needed:
function findByKey(array $keys,array $array){
while(!is_null($key = array_shift($keys))){
if(!isset($array[$key])) return null;
$array = $array[$key];
}
return $array;
}
$values = findByKey(explode('/','foo/bar/baz'),$yourArray);
To modify an array using a path:
function setPath(&$root, $path, $value)
{
$paths = explode('/', $path);
$current = &$root;
foreach ($paths as $path) {
if (isset($current[$path])) {
$current = &$current[$path];
} else {
return null;
}
}
return $current = $value;
}
$path = 'foo/bar/baz';
$root = array('foo' => array('bar' => array('baz' => 'something')));
setPath($root, $path, '123');
You can tweak the function to just return a reference to the element you wish to change:
function &getPath(&$root, $path)
{
$paths = explode('/', $path);
$current = &$root;
foreach ($paths as $path) {
if (isset($current[$path])) {
$current = &$current[$path];
} else {
return null;
}
}
return $current;
}
$x = &getPath($root, $path);
$x = 456; // $root['foo']['bar']['baz'] == 456
A simple loop can make it, like:
function get(array $array, $keys) {
$val = $array;
foreach (explode('/', $keys) as $part) {
if (!isset($val[$part])) {
return null;
}
$val = $val[$part];
}
return $val;
}
$array['foo']['bar']['baz'] = 'something';
echo get($array, 'foo/bar/baz');
http://ideone.com/vcRvXW
Edit:
For modification, just use references:
function set(array &$array, $keys, $value) {
$val = &$array;
foreach (explode('/', $keys) as $part) {
if (!isset($val[$part])) {
$val[$part] = array();
}
$val = &$val[$part];
}
$val = $value;
}
http://ideone.com/WUNhF6

Check if key exists in $_SESSION by building index string

I need to check if a key exists and return its value if it does.
Key can be an array with subkeys or endkey with a value.
$_SESSION['mainKey']['testkey'] = 'value';
var_dump(doesKeyExist('testkey'));
function doesKeyExist($where) {
$parts = explode('/',$where);
$str = '';
for($i = 0,$len = count($parts);$i<$len;$i++) {
$str .= '[\''. $parts[$i] .'\']';
}
$keycheck = '$_SESSION[\'mainKey\']' . $str;
if (isset(${$keycheck})) {
return ${$keycheck};
}
// isset($keycheck) = true, as its non-empty. actual content is not checked
// isset(${$keycheck}) = false, but should be true. ${$var} forces a evaluate content
// isset($_SESSION['mainKey']['testkey']) = true
}
Using PHP 5.3.3.
Instead of building the string, just check if the key exists within your loop.
For instance:
function doesKeyExist($where) {
$parts = explode('/',$where);
$currentPart = $_SESSION['mainKey'];
foreach($parts as $part) {
if (!isset($currentPart[$part])) {
return false;
}
$currentPart = $currentPart[$part];
}
return true;
}
function getByKeys($keys, $array) {
$value = $array;
foreach (explode('/', $keys) as $key) {
if (isset($value[$key])) {
$value = $value[$key];
} else {
return null;
}
}
return $value;
}
Perhaps I'm misunderstanding the question, but this would appear to be the simplest way of doing it:
function getKey($arr, $key) {
if (array_key_exists($key, $arr)) {
return $arr[$key];
} else {
return false;
}
}
$value = getKey($_SESSION['mainKey'], 'testkey');
You should use $$keycheck, not ${$keycheck}.
The last notation is only if you use the variable inside a string (e.g. "${$keycheck}")
See http://php.net/manual/en/language.variables.variable.php for more details about variable variables
You might want to use the eval() php function for this.
function doesKeyExist($where) {
$parts = explode('/',$where);
$str = '';
for($i = 0,$len = count($parts);$i<$len;$i++) {
$str .= '["'. $parts[$i] .'"]';
}
eval('$keycheck = $_SESSION["mainKey"]' . $str . ';');
if (isset($keycheck)) {
return $keycheck;
}
}
HTH

Categories