Changing multidimensional array key value having his keys in another array - php

I have two arrays:
$array = [
'application' => [
'foo' => [
'bar' => 1
]
]
];
$array_2 = [
0 => 'application',
1 => 'foo',
2 => 'bar'
];
How can I change the value of the first array knowning that the last key in the second array is the key who's value must be changed in the first array?
As you can see the second array contains all the keys from the first array. I want to do something like:
$array[$array_2] = 2;
... I suppose I must create a for loop?
For example, if I want to change the bar key value, I must do:
$array['application']['foo']['bar'] = 2;
... but I don't know which key I must change, I only have an array containing keys, and the last key in the list is the key that's value must be changed.

You could build a recursive function, or use a reference:
$result =& $array;
foreach($array_2 as $key) {
$result =& $result[$key];
}
$result = 2;
print_r($array);

This will do it, although not sure what you are trying to achieve.
$array[$array_2[0]][$array_2[1]][$array_2[2]] = 2;

Recursively add the keys. This works -
function get_keys($arr, &$keys){
$keys = array_merge($keys,array_keys($arr));
foreach($arr as $a){
if(is_array($a)){
get_keys($a, $keys);
}
}
}
$array = Array(
'application' => Array(
'foo' => Array(
'bar' => 1
)
)
);
$keys = Array();
get_keys($array, $keys);
var_dump($keys);
OUTPUT-
array
0 => string 'application' (length=11)
1 => string 'foo' (length=3)
2 => string 'bar' (length=3)

Related

Getting multiple specific key value pairs if exist in associate array

I want to convert my Associate Array into array based on certain criteria:
It should contain multiple column (Hence, can't user array_column in this case.)
It should ADD duplicate date
It should remove particular sub-array if NULL. (array_filter)
Input :
array (
0 =>
array (
'date' => "2019-03-31",
'a' => '1',
'b' => '1',
),
1 =>
array (
'date' => "2019-04-02", // If "SAME" date then ADD them
'a' => '1',
'b' => '1',
),
2 =>
array (
'date' => "2019-04-02", // If "SAME" date then ADD them
'a' => '2',
'b' => '1',
),
3 =>
array (
'date' => "2019-04-10",
'a' => '', // If "NULL" then remove the particular sub-array
'b' => '1',
),
4 =>
array (
'date' => "2019-04-18",
'a' => '4',
'b' => '10',
),
)
I've tried the following,
Although I was able to select multiple column in array but this is giving me:
"Duplicate" date.
And including sub-array which has "Null" value.
function colsFromArray(array $array, $keys)
{
if (!is_array($keys)) $keys = [$keys];
return array_map(function ($el) use ($keys) {
$o = [];
foreach($keys as $key){
$o[$key] = isset($el[$key])?$el[$key]:false;
}
return $o;
}, $array);
}
$result = colsFromArray($arr, array("date", "a"));
Required Output :
array (
0 =>
array (
'date' => "2019-03-31",
'a' => '1',
),
1 =>
array (
'date' => "2019-04-02",
'a' => '3',
),
2 =>
array (
'date' => "2019-04-18",
'a' => '4',
),
)
You could try something like this:
function colsFromArray(array $array, $keys)
{
if (!is_array($keys)) $keys = [$keys];
$returnArray = [];
foreach($array as $in){
foreach($keys as $key){
//Check if the key already has a value
if(isset($returnArray[$in['date']][$key]) && is_numeric($returnArray[$in['date']][$key])){
$returnArray[$in['date']][$key] += $in[$key];
}
//If the key is empty, continue
elseif(empty($in[$key])){
continue;
}
//If value is to be set, set date as well
else{
$returnArray[$in['date']]['date'] = $in['date'];
$returnArray[$in['date']][$key] = $in[$key];
}
}
}
//Return array after resetting indexes and removing empty entries
return array_values(array_filter($returnArray));
}
The way that you are passing in the array of columns to your customer function is ambiguous about what should be used to group by and what should be summed. OR if you will never have more than two elements in your 2nd parameter, then your custom function lacks flexibility.
Notice how #SverokAdmin's answer has date hardcoded in the logic, but there is no guarantee that this column name will always exist in the first parameter $array. Ergo, the utility function cannot be reliably used as a utility function.
Consider this custom function which provides your desired result, but allows more meaningful parameter declarations. My snippet allows you to group by one or more column, keep one or more columns in the subarrays, and specify which column should be summed. I cannot make the last column iterable because you state that if a column is missing a value, it should be omitted from the result. In other words, your desired result is unknown if you wanted to sum a and b, but one of the a values was empty.
Code: (Demo)
function colsFromArray(array $array, $groupBy, $keepables, $summable): array
{
$groupBy = (array)$groupBy;
$keepables = (array)$keepables;
// cannot make $summable iterable type because question is unclear on the desired outcome when one of the columns is empty
return array_values(
array_reduce(
$array,
function ($result, $row) use($groupBy, $keepables, $summable) {
$uniqueKey = [];
foreach ($groupBy as $col) {
$uniqueKey[] = $row[$col];
}
$uniqueKey = implode('_', $uniqueKey);
if (!$row[$summable]) {
return $result;
}
foreach ($keepables as $keepable) {
$result[$uniqueKey][$keepable] = $row[$keepable];
}
$result[$uniqueKey][$summable] = ($result[$uniqueKey][$summable] ?? 0) + $row[$summable];
return $result;
},
[]
)
);
}
var_export(
colsFromArray($arr, 'date', 'date', 'a')
);
Output:
array (
0 =>
array (
'date' => '2019-03-31',
'a' => 1,
),
1 =>
array (
'date' => '2019-04-02',
'a' => 3,
),
2 =>
array (
'date' => '2019-04-18',
'a' => 4,
),
)

PHP Nested Arrays: Imploding all the tree keys of each leaf results in a multidimensional array instead of a 1D associative one

From a nested array, I want to generate the 1D associative array which contains, for each leaf, its ascending keys concatenation.
Summary
Expected results example
1.1. Input
1.2. Output
Actual results example
1.1. Input
1.2. Output
Question
Minimal, Testable Executable Sources
4.1. Explanations
4.2. Sources & Execution
Expected results example
Input
The following nested array:
[
'key1' => 'foo',
'key2' => [
'key3' => [
0 => ['key4' => 'bar' ],
1 => ['key4' => 'azerty']
]
]
]
Output
The following 1D associative array (glue character for the concatenation of the keys: _):
[
'key1' => 'foo',
'key2_key3_0_key4' => 'bar',
'key2_key3_1_key4' => 'azerty'
]
Actual results example
Input
[
'etat' => 'bar',
'proposition_en_cours' => [
'fichiers' => [
0 => ['url_fichier' => 'foo' ],
1 => ['url_fichier' => 'bar']
]
]
]
Output
Array
(
[] => bar
[proposition_en_cours] => Array
(
[fichiers] => Array
(
[0] => Array
(
[url_fichier] => foo
)
[1] => Array
(
[url_fichier] => bar
)
)
)
[proposition_en_cours_fichiers] => Array
(
[0] => Array
(
[url_fichier] => foo
)
[1] => Array
(
[url_fichier] => bar
)
)
[proposition_en_cours_fichiers_0] => foo
[proposition_en_cours_fichiers_0_1] => bar
)
Question
As you can see, the array I get differs in all points from the expected one. I can't figure out why.
Minimal, Testable Executable Sources
Explanations
I initialize an array that must contain all the ascending keys for each leaf: $key_in_db_format = [];.
I iterate on the input array. For each element (leaf or subarray), I pop $key_in_db_format if, and only if, the current depth equals the last depth. If it's an array (i.e.: not a leaf): I add the key to $key_in_db_format. I set a value (the leaf) at the key that is the concatenation of the ascending keys.
Sources & Execution
First, define this array in an empty PHP script of your choice:
$values = [
'etat' => 'bar',
'proposition_en_cours' => [
'fichiers' => [
0 => [ 'url_fichier' => 'foo' ],
1 => [ 'url_fichier' => 'bar' ]
]
]
];
Then, copy/paste the following code and you will be able to execute it:
$values_to_insert_in_meta_table = [];
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($values), \RecursiveIteratorIterator::SELF_FIRST);
$last_depth = 0;
$key_in_db_format = [];
foreach ($iterator as $value_key_field => $value_value_field) {
if($iterator->getDepth() == $last_depth) {
array_pop($key_in_db_format);
}
if(is_array($value_value_field)) {
array_push($key_in_db_format, $value_key_field);
} else {
$values_to_insert_in_meta_table[implode('_', $key_in_db_format)] = $value_value_field;
}
$last_depth = $iterator->getDepth();
}
echo '<pre>';
print_r($values_to_insert_in_meta_table);
Maybe I missed something, but as far as I understand, I would do something like that:
<?php
function flatten(array $array, ?string $prefix = null): array {
$prefix = $prefix === null ? '' : "{$prefix}_";
$output = [];
foreach ($array as $key => $value) {
$key = $prefix . $key;
if (is_array($value)) {
$output = array_merge($output, flatten($value, $key));
} else {
$output[$key] = $value;
}
}
return $output;
}
var_export(flatten([
'key1' => 'foo',
'key2' => [
'key3' => [
0 => ['key4' => 'bar' ],
1 => ['key4' => 'azerty']
]
]
]));
Output:
array (
'key1' => 'foo',
'key2_key3_0_key4' => 'bar',
'key2_key3_1_key4' => 'azerty',
)
I think I've found a solution!!! :-)
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($values), \RecursiveIteratorIterator::SELF_FIRST);
$key_in_db_format = [];
$current_counter = 0;
foreach($iterator as $value_key_field => $value_value_field) {
if(is_array($value_value_field)) {
$current_counter = 0;
array_push($key_in_db_format, $value_key_field);
}
if(!is_array($value_value_field)) {
$key_to_insert_in_db = !empty($key_in_db_format) ? implode('_', $key_in_db_format) . '_' . $value_key_field : $value_key_field ;
$values_to_insert_in_meta_table[$key_to_insert_in_db] = $value_value_field;
if($current_counter == count($iterator->getSubIterator())) {
array_pop($key_in_db_format);
}
$current_counter++;
}
}
echo '<br /> <pre>';
print_r($values_to_insert_in_meta_table);
exit;
The idea is:
We add to the array of ascendent keys the key if, and only if, the current element is not a leaf.
If the current element is a leaf, then we define the key equalled to the imploded ascendent keys PLUS (concatenation) the current element's key. Moreover we pop the array of ascendent keys if there are not following siblings elements.

Remove $array[] where key value pair x = x

The output of the below makes $new_array contain multiple arrays with an id, date and type.
$new_array = array();
foreach($things as $thing )(
$new_array[] = array(
'id' => $thing['id'],
'date' => '2017-01-01',
'type' => $thing['type']
);
)
If I print_r( $new_array ) this gets me all the arrays inside, but then I want to modify this array and remove all the arrays inside which don't have a specific type.
To do this I assume I need to unset any $new_array[] arrays where the key value pair type => equals x.
How do I acheive this? I have read into unsetting key value pairs but this doesn't help me with it being multiple arrays.
Perhaps you should check first before adding the array:
foreach($things as $thing ){
if(!empty($thing['type']) && $thing['type'] == 'my type'){
$data[] = [
'id' => $thing['id'],
'date' => '2017-01-01',
'type' => $thing['type']
];
}
}
print_r($data);

PHP multidimensional array get value by key

I have a multi array e.g
$a = array(
'key' => array(
'sub_key' => 'val'
),
'dif_key' => array(
'key' => array(
'sub_key' => 'val'
)
)
);
The real array I have is quite large and the keys are all at different positions.
I've started to write a bunch of nested foreach and if/isset but it's not quite working and feels a bit 'wrong'. I'm fairly familiar with PHP but a bit stuck with this one.
Is there a built in function or a best practise way that I can access all values based on the key name regardless of where it is.
E.g get all values from 'sub_key' regardless of position in array.
EDIT: I see now the problem is that my "sub_key" is an array and therefore not included in the results as per the first comment here http://php.net/manual/en/function.array-walk-recursive.php
Just try with array_walk_recursive:
$output = [];
array_walk_recursive($input, function ($value, $key) use (&$output) {
if ($key === 'sub_key') {
$output[] = $value;
}
});
Output:
array (size=2)
0 => string 'val' (length=3)
1 => string 'val' (length=3)
You can do something like
$a = [
'key' => [
'sub_key' => 'val'
],
'dif_key' => [
'key' => [
'sub_key' => 'val'
]
]
];
$values = [];
array_walk_recursive($a, function($v, $k, $u) use (&$values){
if($k == "sub_key") {
$values[] = $v;
}
}, $values );
print_r($values);
How does it work?
array_walk_recursive() walks by every element of an array resursivly and you can apply user defined function. I created an anonymous function and pass an empty array via reference. In this anonymous function I check if element key is correct and if so it adds element to array. After function is applied to every element you will have values of each key that is equal to "sub_key"

Search and replace in multidimensional array

I have below multidimensional array and I want to replace value of
$data['meta']['attr']['road'] with an array ['test']
Thing is I don't know keys they are only available through keys array
$keys = ['meta', 'attr', 'road'];
This is just an example keys might be anything hence want to search each element, check it and replace if key is found
My multidimensional array is below:
$data = ['meta' => [
'time' => 11.364,
'count' => 3,
'attr' => [
'id'=> 7845,
'road' => [
'length' => 'km',
'width' => 'm'
]
]
],
'Assets' => [15,78,89]
];
Looks complicated search and replace algorithm really stuck ...any thoughts?
$keys = ['meta', 'attr', 'road'];
$arr = &$data;
foreach($keys as $key)
{
$arr = &$arr[$key];
}
$arr = ['test'];
You can access multidimensional array values using brackets and keys.
// set
$someArray['key']['key'] = 'value';
// get
$someVar = $someArray['key']['key'];
See arrays section on Php reference
So in your case it is;
$data['meta']['attr']['road'] = array('test' => 'value');

Categories