PHP: Convert a standard array into associate array keys? - php

I'm working on a function that takes an array as a parameter, and then calls the value of a different associative array using the input array as keys. So for example,
array('level_1', 'level_2', 'level_3')
should become
$variable_defined_within_function['level_1']['level_2']['level_3']
I have a way to do this that I think will work, but it feels hacky and weird and I don't really want to use eval() unless I absolutely must.
function fetch($keys) {
if (!is_array($keys)) { $variable = array($keys); }
foreach ($keys as $key) {
$assoc_string .= '[' . str_replace('\'' . '\\\'' . $key) . ']';
}
$reqstring = 'if (isset($this->vars' . $assoc_string . ')) { return $this->vars' . $assoc_string . '; } else { return false; }';
eval($reqstring);
}
That just doesn't seem right, does it? How could I convert a list of keys into an associative array?

How about something like this:
function fetch($keys) {
if (!is_array($keys))
$keys = array($keys);
$arr = $this->vars;
foreach($keys as $key)
{
if (!isset($arr[$key]))
return FALSE;
$arr = $arr[$key];
}
return $arr;
}

Please consider this function as a starting point:
function fetch(array $keys, array $array) {
$pointer = &$array;
foreach ($keys as $key) {
if (!isset($pointer[$key]))
break;
$pointer = &$pointer[$key];
}
return $pointer;
}
it will loop through $array with provided $keys and return the value of the last existing key. You can use it as a base and add your logic for keys that not exists or something

Here is the solution, very simple, but yet, still very confusing sometimes.
$arr = array('level_1', 'level_2', 'level_3');
function fetch(array $array){
$numberOfDimensions = count($array);
// the value of array['level_1']['level_2']['level_3']
$value = "something";
for($i = $numberOfDimensions-1; $i >= 0; $i--){
$value = array($array[$i] => $value);
}
return $value;
}
print_r(fetch($arr));
Output:
Array ( [level_1] => Array ( [level_2] => Array ( [level_3] => something )))
As you can see, the solution is very simple, but to understand what is going on, you must understand how array works.
Every array has index, or hash when talking about associative arrays, and for each of those keys there is only exactly one value. The value can be of any type, so if we add an array as value of another array's element, you get 2-dimensional array. If you add 2-dimensional array as value of another arrays's element, you get 3-dimensional array. By repeating the process, you get N-dimensional array.
The algorithm works by going from the deepest key (the last element inside keys array) and assigning a new associative array to the $value variable, which is the value prepared to be set as array value of dimension above, all until the end of loop.
Lets have a look at the changes made to variable $value inside for loop, before and after change.
The initial value of variable $value is "something". "something" is value of array level_3, and so on...
So, running
print_r(array['level_1']['level_2']['level_3']);
will produce
something
Here is a full state view of the $value variable inside for loop:
Key: level_3
something
Array ( [level_3] => something )
Key: level_2
Array ( [level_3] => something )
Array ( [level_2] => Array ( [level_3] => something ) )
Key: level_1
Array ( [level_2] => Array ( [level_3] => something ) )
Array ( [level_1] => Array ( [level_2] => Array ( [level_3] => something ) ) )

Related

Build dynamic associative array from simple array in php

I have an array as given below
$arr = ['Product', 'Category', 'Rule'];
This can be a dynamic array meaning it can sometimes have between 1-5 elements inside it and its value can change.
How can we create an array as given below from the above one in a dynamic manner.
$json['Product']['Category']['Rule'] = 'fixed';
Simply put am just trying to make a multidimensional array from the values I get from the $arr.
This function should do it.
function nestArray($arr, $value) {
if (!count($arr)) {
return $value;
}
foreach (array_reverse($arr) as $key) {
$new = [$key => $value];
$value = $new;
}
return $new;
}
Example
$arr = ['Product', 'Category', 'Rule'];
$nested = nestArray($arr, 'fixed');
print_r($nested);
Output
Array
(
[Product] => Array
(
[Category] => Array
(
[Rule] => fixed
)
)
)

PHP Search multidimensional array for value & get corresponding element value

I am using PHP & I have a multi dimensional array which I need to search to see if the value of a "key" exists and if it does then get the value of the "field". Here's my array:
Array
(
[0] => Array
(
[key] => 31
[field] => CONSTRUCTN
[value] => LFD_CONSTRUCTION_2
)
[1] => Array
(
[key] => 32
[field] => COOLING
value] => LFD_COOLING_1
)
)
I want to be able to search the array for the "key" value of 31. If it exists, then I want to be able to extract the corresponding "field" value of "CONSTRUCTN".
I've tried using array_search(31, $myArray) but it does not work...
function searchMultiArray($val, $array) {
foreach ($array as $element) {
if ($element['key'] == $val) {
return $element['field'];
}
}
return null;
}
And then:
searchMultiArray(31, $myArray);
Should return "CONSTRUCTN".
One-line solution using array_column and array_search functions:
$result = array_search(31, array_column($arr, 'key', 'field'));
print_r($result); // CONSTRUCTN
Or with simple foreach loop:
$search_key = 31;
$result = "";
foreach ($arr as $item) { // $arr is your initial array
if ($item['key'] == $search_key) {
$result = $item['field'];
break;
}
}
print_r($result); // CONSTRUCTN
I haven't tested, but I think this should do it.
function searchByKey($value, $Array){
foreach ($Array as $innerArray) {
if ($innerArray['key'] == $value) {
return $innerArray['field'];
}
}
}
Then calling searchByKey(31, $myArray); should return 'CONSTRUCTN'.
One liner solution:
$result = is_numeric($res = array_search(31, array_column($myArray, 'key'))) ? $myArray[$res]["field"] : "";
array_search accepts 2 parameters i.e the value to be search and the array, subsequently I've provided the array which needs searching using array_column which gets that particular column from the array to be searched and is_numeric is used to make sure that valid key is returned so that result can displayed accordingly.

return all keys of a associative array with a function

I have an associative array which looks like this:
Array (
[0] => Array ( )
[1] => Array ( )
[2] => Array ( [318] => 3.3333333333333 )
[3] => Array ( )
[4] => Array ( )
[5] => Array ( [317] => 5 )
)
I want to return all the array keys of the array as number, not string; thats why I am not echoing it. This is how I am trying:
function user_rated_posts(){
global $author;
if(isset($_GET['author_name'])) :
$curauth = get_userdatabylogin($author_name);
else :
$curauth = get_userdata(intval($author));
endif;
$user_rated_posts = get_user_meta($curauth->ID, 'plgn_rating',true);
foreach ($user_rated_posts as $arrs){
foreach($arrs as $key=> $value){
$keys= $key;
}
}
return $keys;
}
when I call the function like this:
array( explode(',',user_rated_posts()) )
I am only getting this
array(317)
I am trying to get all the keys in comma separated format, like:
array(318, 317)
Thanks.
You're overwriting the $keys variable each time you go through your loop, so it's always only set to the last one.
$keys = array();
foreach ($user_rated_posts as $arrs) {
foreach($arrs as $key=> $value){
$keys[] = $key;
}
}
return $keys;
... that will return an actual array structure, if you actually want a comma separated list then return implode(', ', $keys); instead.
You could use array_keys($array) instead of looping twice.
$keys = array();
foreach ($user_rated_posts as $arrs) {
$keys = array_merge($keys, array_keys($array));
}
return $keys;
I came up with a way that avoids the nested looping in favor of PHP's built in functions:
$result = array_map(array_keys,$user_rated_posts);
$result2 = array_map(implode, $result);
$result3 = array_filter($result2);
First line iterates over the array, returning the keys. Second line reduces the sub-array to strings. Third line removes the empty values.
Here is a working version: https://eval.in/99119
Added bonus: keeps the positions where the values were found as the keys like:
array(2) {
[2]=>
string(3) "318"
[5]=>
string(3) "317"
}

Multidimenssion array compare two arrays and update first array value

I want to filter a array by a number and update its status in the first array.
I have two array $arr1,$arr2
$arr1 = array(
0=>array('number'=>100,name=>'john'),
1=>array('number'=>200,name=>'johnny')
);
$arr2= array(
0=>array('number'=>300,name=>'r'),
1=>array('number'=>100,name=>'b'),
2=>array('number'=>200,name=>'c')
);
Final output should be an array like this
$arr1 = array(
0=>array('number'=>100,name=>'b'),
1=>array('number'=>200,name=>'c')
);
Any ideas to start off please ?
For specialized array modifications like this, the method of choice is array walk. It allows you to apply a custom function to each element in a given array.
Now, because of your data format, you will have to do a loop. Wrikken is asking if you can retrieve or transform the data to provide faster access. The algorithm below is O(n^2): it will require as many cycles as there are elements in the first array times the number of elements in the second array, or exactly count($arr1) * count($arr2).
function updateNameFromArray($element, $key, $arr2) {
foreach($arr2 as $value) {
if($value['number'] == $element['number']) {
$element['name'] == $value['name'];
break;
}
}
}
array_walk($arr1, "updateNameFromArray", $arr2);
Now, what Wrikken is suggesting is that if your arrays can be changed to be keyed on the 'number' property instead, then the search/replace operation is much easier. So if this were your data instead:
$arr1 = array(
100=>array('number'=>100,name=>'john'),
200=>array('number'=>200,name=>'johnny')
);
// notice the keys are 100 and 200 instead of 0,1
$arr2= array(
300=>array('number'=>300,name=>'r'),
100=>array('number'=>100,name=>'b'),
200=>array('number'=>200,name=>'c')
);
// notice the keys are 300, 100 and 200 instead of 0,1, 2
Then you could do this in O(n) time, with only looping over the first array.
foreach($arr1 as $key => $value) {
if(isset($arr2[$key])) {
$value['number'] = $arr2[$key]['number'];
}
}
Try this. It's not that clean but i think it would work.
<?php
$arr1 = array(0=>array('number'=>100,'name'=>'john'),1=>array('number'=>200,'name'=>'johnny'));
$arr2= array(0=>array('number'=>300,'name'=>'r'),1=>array('number'=>100,'name'=>'b'),2=>array('number'=>200,'name'=>'c'));
foreach( $arr1 as $key=>$data1 )
{
foreach( $arr2 as $key2=>$data2 )
{
if( $data1['number'] == $data2['number'] )
{
$arr1[$key]['name'] = $arr2[$key2]['name'];
}
}
}
print_r( $arr1 );
?>
the output would be :
Array
(
[0] => Array
(
[number] => 100
[name] => b
)
[1] => Array
(
[number] => 200
[name] => c
)
)
There isn't really a simple way for this to be accomplished with generic PHP functions, so, You might need to create mapping arrays.
The way I would approach this, is creating a loop that goes through the first array, and maps the number value as a key to the index of it's place in $arr1 giving you:
$tmp1 = array();
foreach ($arr1 as $key => $number_name) {
$tmp1[$number_name['number']] = $key;
}
This should give you an array that looks like
$tmp1 [
100 => 0,
200 => 1
];
Then I would loop through the second array, get the number value, if that existed as a key in $tmp1, get the associated value (being the key for $arr1), and use that to update the name in $arr1.
// Loop through $arr2
foreach ($arr2 as $number_name) {
// Get the number value
$number = $number_name['number'];
// Find the $arr1 index
if (isset($tmp1[$number])) {
$arr1_key = $tmp1[$number];
// Set the $arr1 name value
$arr1[$arr1_key]['name'] = $number_name['name'];
}
}
<?php
//Set the arrays
$arr1 = array(
array('number'=>100,'name'=>'john'),
array('number'=>200,'name'=>'johnny')
);
$arr2= array(
array('number'=>300,'name'=>'r'),
array('number'=>100,'name'=>'b'),
array('number'=>200,'name'=>'c')
);
// use a nested for loop to iterate and compare both arrays
for ($i=0;$i<count($arr1);$i++):
for ($j=0;$j<count($arr2);$j++):
if ($arr2[$j]['number']==$arr1[$i]['number'])
$arr1[$i]['name']=$arr2[$j]['name'];
endfor;
endfor;
print_r($arr1);
OUTPUT:
Array (
[0] => Array ( [number] => 100 [name] => b )
[1] => Array ( [number] => 200 [name] => c )
)
That being said, you should probably reconsider the very way your data is structured. Do you really need a multi-dimensional array or can you use a simple associative array, like so:
// set the arrays
$arr1 = array(
'john'=>100,
'johnny'=>200
);
$arr2 = array(
'r'=>300,
'b'=>100,
'c'=>200
);
// find values in arr2 common to both arrays
$arr3 = array_intersect($arr2, $arr1);
// change the key of arr1 to match the corresponding key in arr2
foreach ($arr3 as $key=>$value) {
$old_key = array_search($value, $arr1);
$arr1[$key]=$arr1[$old_key];
unset($arr1[$old_key]);
}
print_r($arr1);
OUTPUT:
Array (
[b] => 100
[c] => 200
)

How to create a tree-like array from a loop?

Suppose I have a one-dimensional array $arr like this:
Array
(
['key1'] => 1
['key2'] => 1
['key3'] => 1
['key4'] => 1
)
I want to loop through $arr like this:
foreach($arr as $key => $a) {
//$tree[????] = ????? Here is my concern
}
such that, doing print_r($tree) produces
Array
(
['key1'] => Array
(
['key2'] => Array
(
['key3'] => Array
(
['key4'] => 1
)
)
)
)
My concern is how to increase the dimension (not the values) of the array $tree inside the loop from a single dimension array $numbers such that $tree['key1']['key2']['key3'] is an array and $tree['key1']['key2']['key3']['key4'] is equal to 1.
Furthermore, if I have a single dimensional array $foo with 10 elements. I should produce another array $bar that expands to 10 dimensional array similar to the output above.
What should I do inside the foreach loop? Or instead of using loop, is there a way to produce an output like that of above from a one dimension array?
EDIT:
OK well you obviously need some kind of recursive function, but can
you explain how you know that key2 should be a child of key1, key3
should be a child of key2 etc based on the data you show?
The next element of a 1D array is a child of the previous element. So if the 1D array is like this:
Array
(
['bar'] => 1 // I don't care of the values as of the moment
['foo'] => 1
['baz'] => 1
)
Element foo should be the child of bar, and element baz should be the child of foo for the tree array.
Ok so the actual values of everything except the last element are
irrelevant?
Actually all values of the 1D array is irrelevant as of this moment. I am only concerned on constructing the tree array.
The way to do this is to use references.
function create_tree($arr) {
$result = array();
$ref = &$result;
foreach ($arr as $key => $el) {
$ref = array($key => $el);
$ref =& $ref[$key];
}
return $result;
}
How it works:
$result = array(); // an array to hold the result
$ref = &$result; // start with a reference to the top level
foreach ($arr as $key => $el) { // iterate over the input array
$ref = array($key => $el); // create this level in the array
$ref =& $ref[$key]; // change the reference to be the new deepest level
}
return $result; // return the result
See it working
Assignment by reference appears to be useful for this.
$tree = array();
$node =& $tree;
foreach ($arr as $key => $a) {
$node =& $node[$key];
$node = array();
}
$node = end($arr);

Categories