I'm trying to find (or create) a function. I have a multidimensional array:
$data_arr = [
"a" => [
"aa" => "abfoo",
"ab" => [
"aba" => "abafoo",
"abb" => "abbfoo",
"abc" => "abcfoo"
],
"ac" => "acfoo"
],
"b" => [
"ba" => "bafoo",
"bb" => "bbfoo",
"bc" => "bcfoo"
],
"c" => [
"ca" => "cafoo",
"cb" => "cbfoo",
"cc" => "ccfoo"
]
];
And I want to access a value using a single-dimentional array, like this:
$data_arr_call = ["a", "ab", "abc"];
someFunction( $data_arr, $data_arr_call ); // should return "abcfoo"
This seems like there's probably already a function for this type of thing, I just don't know what to search for.
Try this
function flatCall($data_arr, $data_arr_call){
$current = $data_arr;
foreach($data_arr_call as $key){
$current = $current[$key];
}
return $current;
}
OP's Explanation:
The $current variable gets iteratively built up, like so:
flatCall($data_arr, ['a','ab','abc']);
1st iteration: $current = $data_arr['a'];
2nd iteration: $current = $data_arr['a']['ab'];
3rd iteration: $current = $data_arr['a']['ab']['abc'];
You could also do if ( isset($current) ) ... in each iteration to provide an error-check.
You can use this function that avoids the copy of the whole array (using references), is able to return a NULL value (using array_key_exists instead of isset), and that throws an exception when the path doesn't exist:
function getItem(&$array, $path) {
$target = &$array;
foreach($path as $key) {
if (array_key_exists($key, $target))
$target = &$target[$key];
else throw new Exception('Undefined path: ["' . implode('","', $path) . '"]');
}
return $target;
}
demo:
$data = [
"a" => [
"aa" => "abfoo",
"ab" => [
"aba" => "abafoo",
"abb" => NULL,
"abc" => false
]
]
];
var_dump(getItem($data, ['a', 'ab', 'aba']));
# string(6) "abafoo"
var_dump(getItem($data, ['a', 'ab', 'abb']));
# NULL
var_dump(getItem($data, ['a', 'ab', 'abc']));
# bool(false)
try {
getItem($data, ['a', 'ab', 'abe']);
} catch(Exception $e) {
echo $e->getMessage();
}
# Undefined path: ["a","ab","abe"]
Note that this function can be improved, for example you can test if the parameters are arrays.
Wanted to post an even more elegant solution: array_reduce
$data_arr = [
"a" => [
...
"ab" => [
...
"abc" => "abcfoo"
],
...
],
...
];
$result = array_reduce(["a", "ab", "abc"], function($a, $b) {
return $a[$b];
}, $data_arr);
// returns "abcfoo"
I've been using Javascript's Array.reduce() a lot lately in updating some legacy code to ES6:
JS:
const data_obj = {...};
let result = ['a','ab','abc'].reduce((a, b) => a[b], data_obj);
You need a function like this:
function getValue($data_arr, $data_arr_call) {
foreach ($data_arr_call as $index) {
if (isset($data_arr[$index])) {
$data_arr = $data_arr[$index];
} else {
return false;
}
}
return $data_arr;
}
And use it like this:
$data_arr = [
"a" => [
"ab" => [
"abc" => "abbfoo",
],
],
];
$data_arr_call = ["a", "ab", "abc"];
$value = getValue($data_arr, $data_arr_call);
if ($value) {
// do your stuff
}
Related
I would like te perform a calculation on the following array
$array = [
"calculation" => [
"add" => [
"ceil" => [
"multiply" => [
9.95,
"90%"
]
],
0.95
]
]
];
eventually the calculation would traverse into:
1. add(ceil(multiply(9.95, 90%)), 0.95)
2. ceil(multiply(9.95, 90%)) + 0.95
3. ceil(8.955) + 0.95
4. 9 + 0.95
5. 9.95
I've tried recursively looping through the array using array_walk_recursive and custom functions which basically to the same.
but the problem is that the add cannot calculate the ceil before it has calculated the multiply.
So how can i recusively in reversed order calculate all the parts of the array?
I'm slowly loosing my mind over this and starting to question if it's even possible.
All ideas are greatly appreciated
Another option, that verify the indexes of the array are valid functions and allows add and multiple with more than 2 values.
function multiply(...$a)
{
return array_product($a);
}
function add(...$a)
{
return array_sum($a);
}
function applyCalculation(array $data)
{
$firstKey = array_keys($data)[0];
$arguments = [];
foreach ($data[$firstKey] as $name => $value) {
// if argument is array with existing function, we pass it recursively
if (function_exists($name) && is_array($value)) {
$result = applyCalculation([$name => $value]);
$result = is_array($result) ? $result : [$result];
$arguments = array_merge($arguments, $result);
} elseif (!is_array($value)) {
// if not array, just append to arguments
$value = strpos($value, '%') !== false ? str_replace('%','',$value) / 100 : $value;
$arguments[] = $value;
} elseif (is_array($value)) {
// error here, the index is not a valid function
}
}
return $firstKey(...$arguments);
}
echo applyCalculation($array['calculation']);
So it will work for your example:
$data = [
"calculation" => [
"add" => [
"ceil" => [
"multiply" => [
9.95,
'90%',
]
],
0.95
]
]
];
// will give you 9.95
But it also works for more complex cases::
$array = [
"calculation" => [
"add" => [
"ceil" => [
"add" => [
"multiply" => [
9.95,
'90%'
],
"add" => [
1,
3
],
]
],
0.95,
3
]
]
];
// will give you 16.95
Assuming that you have this structure, you may use this code. If you need more than 2 values to be "added" or "multiplied" you may need to use some splat operator or change the logic a little bit:
<?php
$array = [
"calculation" => [
"add" => [
"ceil" => [
"multiply" => [
9.95,
"90%"
]
],
0.95
]
]
];
function add($a, $b) {
return $a + $b;
}
function multiply($a, $b) {
return $a * $b;
}
function calculation($arr, $operation = null) {
$elements = [];
foreach($arr as $k => $value) {
if (is_numeric($k)) {
// change % with valid numeric value
if(strpos($value, '%')) {
$value = str_replace('%','',$value) / 100;
}
//if there are no operations, append to array to evaluate under the operation
$elements[] = $value;
} else {
//if there are operations, call the function recursively
$elements[] = calculation($value, $k);
}
}
if ($operation !== null) {
return call_user_func_array($operation, $elements);
}
return $elements[0];
}
echo calculation($array['calculation']);
I have given the array:
array(
"firstName": null,
"lastName": null,
"category": [
"name": null,
"service": [
"foo" => [
"bar" => null
]
]
]
)
that needs to be transform into this:
array(
0 => "firstName",
1 => "lastName",
2 => "category",
"category" => [
0 => "name",
1 => "service",
"service" => [
0 => "foo",
"foo" => [
0 => "bar"
]
]
]
)
The loop should check if a value is an array and if so, it should add the key as a value (0 => category) to the root of array and then leave the key as it is (category => ...) and traverse the value again to build the tree as in example.
I am stuck with this and every time I try, I get wrong results. Is there someone who is array guru and knows how to simply do it?
The code so far:
private $array = [];
private function prepareFields(array $fields):array
{
foreach($fields as $key => $value)
{
if(is_array($value))
{
$this->array[] = $key;
$this->array[$key] = $this->prepareFields($value);
}
else
{
$this->array[] = $key;
}
}
return $this->array;
}
You could make use of array_reduce:
function prepareFields(array $array): array
{
return array_reduce(array_keys($array), function ($result, $key) use ($array) {
$result[] = $key;
if (is_array($array[$key])) {
$result[$key] = prepareFields($array[$key]);
}
return $result;
});
}
Demo: https://3v4l.org/3BfKD
You can do it with this, check the Demo
function array_format(&$array){
$temp_array = [];
foreach($array as $k=>$v){
$temp_array[] = $k;
if(is_array($v)){
array_format($v);
$temp_array[$k] = $v;
}
}
$array = $temp_array;
}
array_format($array);
print_r($array);
I want to combine two different multi-dimensional arrays, with one providing the correct structure (keys) and the other one the data to fill it (values).
Notice that I can't control how the arrays are formed, the structure might vary in different situations.
$structure = [
"a",
"b" => [
"b1",
"b2" => [
"b21",
"b22"
]
]
];
$data = [A, B1, B21, B22];
Expected result:
$array = [
"a" => "A",
"b" => [
"b1" => "B1",
"b2" => [
"b21" => "B21",
"b22" => "B22"
]
]
];
You can use the following code, however it will only work if number of elements in $data is same or more than $structure.
$filled = 0;
array_walk_recursive ($structure, function (&$val) use (&$filled, $data) {
$val = array( $val => $data[ $filled ] );
$filled++;
});
print_r( $structure );
Here is a working demo
You can try by a recursive way. Write a recursive method which takes an array as first argument to alter and the data set as its second argument. This method itself call when any array element is another array, else it alters the key and value with the help of data set.
$structure = [
"a",
"b" => [
"b1",
"b2" => [
"b21",
"b22"
]
]
];
$data = ['A', 'B1', 'B21', 'B22'];
function alterKey(&$arr, $data) {
foreach ($arr as $key => $val) {
if (!is_array($val)) {
$data_key = array_search(strtoupper($val), $data);
$arr[$val] = $data[$data_key];
unset($arr[$key]);
} else {
$arr[$key] = alterKey($val, $data);
}
}
ksort($arr);
return $arr;
}
alterKey($structure, $data);
echo '<pre>', print_r($structure);
Working demo.
This should work.
$structure = [
"a",
"b" => [
"b1",
"b2" => [
"b21",
"b22"
]
]
];
$new_structure = array();
foreach($structure as $key =>$value)
{
if(!is_array($value))
{
$new_structure[$value]= $value;
}else{
foreach($value as $k =>$v)
{
if(!is_array($v))
{
$new_structure[$key][$v]=$v;
}else
{
foreach($v as $kk => $vv)
{
$new_structure[$k][$vv]=$vv;
}
}
}
}
}
print_r($new_structure);exit;
Use
$array=array_merge($structure,$data);
for more information follow this link
how to join two multidimensional arrays in php
I have an array that looks something like this:
array:2 [
"test1_test2_test3" => "result"
"category_slug" => "the_slug"
]
What I need to do is convert it to a multidimensional array that would look something like this:
array:2 [
"test1" => [
"test2" => [
"test3" => "result"
]
]
"category" => [
"slug" => "the_slug"
]
]
I know that I can explode on the key to get an array but am unsure of how to then go from this to end up with the final result.
EDIT The array looks like that initially because it's being pulled from request parameters: http://url.com?test1.test2.test3=result&category.slug=the_slug and Laravel auto converts it to an array.
A simple solution:
$result = [];
foreach ($array as $key => $value) {
foreach (array_reverse(explode('_', $key)) as $key_part) {
$value = [$key_part => $value];
}
$result += $value;
}
If you need to handle several keys with the same parts (such as test1_test2_test3 and test1_test2_test4), replace the last line with:
$result = array_merge_recursive($result, $value);
My approach would be to reverse the array, then loop through the keys and nest them.
The code below should do the trick.
$array = [
"test1_test2_test3" => "result",
"category_slug" => "the_slug"
];
$array = array_map(function ($key, $value) {
$keys = array_reverse(explode('_', $key));
while($key = array_shift($keys)) {
$value = [$key => $value];
}
return $value;
}, array_keys($array), $array);
$array = call_user_func_array('array_merge', $array);
var_dump($array);
/**
array(2) {
["test1"]=>
array(1) {
["test2"]=>
array(1) {
["test3"]=>
string(6) "result"
}
}
["category"]=>
array(1) {
["slug"]=>
string(8) "the_slug"
}
}
*/
One way to go:
$arr = array("test1_test2_test3" => "result", "category_slug" => "the_slug");
$res = array();
foreach($arr as $k=>$v) {
$t = explode("_", $k);
$new_arr = array();
$tmp = $v;
for($i=count($t)-1; $i > 0; $i--) {
$new_arr[$t[$i]] = $tmp;
$tmp = $new_arr;
$new_arr = array();
}
$res[$t[0]] = $tmp;
}
print_r($res);
Result:
Array
(
[test1] => Array
(
[test2] => Array
(
[test3] => result
)
)
[category] => Array
(
[slug] => the_slug
)
)
Looking through the Laravel documentation I found a helper array_set, which means in order to change the key to a multidimensional array I can change the key to use dot notation with str_replace and then use the helper to convert it over:
$array = [
"test1_test2_test3" => "result"
"category_slug" => "the_slug"
]
$new_array = []
foreach($array as $key => $value)
{
$key = str_replace('_', '.', $key);
array_set($new_array, $key, $value);
}
Result:
array:2 [
"test1" => [
"test2" => [
"test3" => "result"
]
]
"category" => [
"slug" => "the_slug"
]
]
How can I obtain a key in a array just by knowing it's value? For example, here is an array:
$array = Array("Item1" => array("Number" => "One", "Letter" => "A"));
Just by knowing "One" or "A", how can I get the main key name, Item1?
I've looked into array_key_value and in_array but I do not think that those functions are helpful for my kind of array.
Since it is a 2d array, you will want to search the inner array for the value so you would have to make your own function to do this. Something like this:
function findInArray($array, $lookup){
//loop over the outer array getting each key and value.
foreach($array as $key=>$value){
//if we found our lookup value in the inner array
if(in_array($lookup, $value)){
//return the original key
return $key;
}
}
//else, return null because not found
return null;
}
$array = Array("Item1" => array("Number" => "One", "Letter" => "A"));
var_dump(findInArray($array, 'One')); //outputs string(5) "Item1"
var_dump(findInArray($array, 'Two')); //outputs null
Demo: https://3v4l.org/oRjHK
This function may help you
function key_of_value($array, $value){
foreach($array as $key=>$val){
if(in_array($value, $val)){
return $key;
}
}
return null;
}
echo key_of_value(['Item1'=>['One','Two','Three','Hello',2,6]],'A');
There is no way around iterating through your data. This might be a little more elegant than two foreach loops:
<?php
$match = null;
$needle = 'Two';
$haystack = [
'Item1' => [
'Number' => 'One',
'Letter' => 'A'
],
'Item2' => [
'Number' => 'Two',
'Letter' => 'B'
],
'Item3' => [
'Number' => 'Three',
'Letter' => 'C'
],
];
array_walk($haystack, function($entry, $key) use ($needle, &$match) {
if(in_array($needle, $entry)) {
$match = $key;
}
});
var_dump($match);
The output obviously is:
string(5) "Item2"
You can use array_walk_recursive to iterate on array values recursive. I write a function that return main key of searched value in nested arrays.
<?php
$array = array("Item1" => array("Number" => "One", "Letter" => "A", 'other' => array('Number' => "Two")));
echo find_main_key($array, 'One'); //Output: "Item1"
echo find_main_key($array, 'A'); //Output: "Item1"
echo find_main_key($array, 'Two'); //Output: "Item1"
var_dump(find_main_key($array, 'nothing')); // NULL
function find_main_key($array, $value) {
$finded_key = NULL;
foreach($array as $this_main_key => $array_item) {
if(!$finded_key) {
array_walk_recursive($array_item, function($inner_item, $inner_key) use ($value, $this_main_key, &$finded_key){
if($inner_item === $value) {
$finded_key = $this_main_key;
return;
}
});
}
}
return $finded_key;
}
This is how I would do it:
foreach($array as $key => $value) {
if(in_array('One', $value)) echo $key;
}