Add and modify value in a deep nested mixed array - php

Let's say you have an array like this:
$list = array(
'name' => 'foobar',
'id' => '12302',
'group' => array(array(
'name' => 'teamA',
'members' => array(
array(
'ID' => 'OAHSJLASJ8888'
'name' => 'eric',
'fname' => 'lu',
'age' => '22'
),
array(
'ID' => 'OKZ8JJLJYYH6'
'name' => 'franz',
'fname' => 'as',
'age' => '33'
),
array(
'ID' => 'OKOIYHJKKK'
'name' => 'Amr',
'fname' => 'ok',
'age' => '13'
)
)
),
array(
'name' => 'teamB',
'members' => array(
array(
'ID' => 'FGZ9ILKA'
'name' => 'Evan',
'fname' => 'lu',
'age' => '22'
),
array(
'ID' => 'KMLML2039KKK'
'name' => 'Michel',
'fname' => 'as',
'age' => '33'
),
array(
'ID' => 'AAA2039KKK'
'name' => 'Nickr',
'fname' => 'ok',
'age' => '13'
)
)
)
)
);
You want to add a value to the associative array named Amr which is the third element of the member key of the group key $list[group][0][members][2][newKey] = B
Using recursive function and foreach, I'm able to find anything I'm aiming at. Using array_walk_recursive I can also find the targeted key value and modify it.
Using RecursiveIteratorIterator and foreach, I can also find the element and modify it's value.
My issue is that I can not replace the modified object within the tree. I can follow the path down, but I'm not able to climb the tree back. I could maintain a index of each array I traverse and then recalculate the path to the key, but it looks culprit to me.
I can not modify the data structure, the dataset I have is as is.
Thanks for any help you could bring.
Code for Iterator:
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($list));
foreach($iterator as $key=>$value) {
if ($key === 'ID') {
$metas = get_relatedmeta_objects($value),true));
//metas key should be added to the current array
}
}
Recursive method:
function searchKeyAndAdd( &$element) {
if(is_array($element) || is_object($element)){
foreach ( $element as &$key => $value ) {
if ($key === "ID") {
$metas = get_relatedmeta_objects($value);
//metas key should be added to the current array
} else if (is_array($value)) {
searchObject($value);
}
}
}
}
array_walk_recursive method:
function alterArray(&$item, $key, &$parentRec) {
if (is_array($item) || is_object($item)) {
searchObject($item);
}
if ($key === 'ID') {
$parentRec = json_decode(json_encode($parentRec), true);
$parentRec['metas'] = get_field_objects($item);
// the current array is modified but the value does not go back to the $list initial array.
}
}
function searchObject( &$element, &$parent) {
array_walk_recursive($element, 'alterArray', $element);
}
The data set could be anything. You do not know the key, you just know that some nested object can have ID key and when they do you want to add more content to this object.

The recursive function can do it, but you should use the & prefix on $value instead of $key:
function searchKeyAndAdd( &$element) {
if(is_array($element) || is_object($element)){
foreach ( $element as $key => &$value ) {
if ($key === "ID") {
$element['meta'] = get_relatedmeta_objects($value);
} else {
searchKeyAndAdd($value);
}
}
}
}
searchKeyAndAdd($list);
The other two methods offer no reference to the parent, although in the case of array_walk_recursive you tried it with the third argument, but there things get messy: to make it work on each recursive depth, you call array_walk_recursive recursively... but array_walk_recursive already visits all the key/value pairs recursively. So this will lead to many calls to alterArray with the same key/value, but with a different ancestor as third argument for each of them.
Furthermore, with this line:
$parentRec = json_decode(json_encode($parentRec), true);
... you lose the reference to the original $parentRec, and so any modification you make to $parentRec will no longer have an effect on the original array.

Related

How to find a key in multidimensional array?

I have a multidimensional array and i need make a searchCategory($categories, $id) function, which have to return a value of 'title' properties.
it try this code, it work but only for one layer of multidimensional array.
Multidimensional array:
$categories = array( array(
"id" => 1,
"title" => "Обувь",
'children' => array(
array(
'id' => 2,
'title' => 'Ботинки',
'children' => array(
array('id' => 3, 'title' => 'Кожа'),
array('id' => 4, 'title' => 'Текстиль'),
),
),
array('id' => 5, 'title' => 'Кроссовки',),
)), array(
"id" => 6,
"title" => "Спорт",
'children' => array(
array(
'id' => 7,
'title' => 'Мячи'
)
) ), );
Code which i try solve problem:
function searchCategory($categories, $id) {
foreach($categories as $category) {
if($category['id'] == $id) {
echo $category['title'] . '<br>';
}
}
};
I need my function to look up the id value in all arrays and return the title in the case of the array found
Here is recursive iterator case for you, please go through inline doc for explanation
function searchCategory($categories, $id)
{
$arrayiter = new RecursiveArrayIterator($categories);
$iteriter = new RecursiveIteratorIterator($arrayiter);
foreach ($iteriter as $key => $value) {
// checking if iterator comes to point where key is id and value matched
if ($key == 'id' && $value == $id) {
// returning matched value with current iterator instance
return $iteriter->getInnerIterator()['title'];
}
}
return '';
}
echo searchCategory($categories, 2).'<br/>';
echo searchCategory($categories, 7);
Working demo.
RecursiveArrayIterator - This iterator allows to unset and modify values and keys while iterating over Arrays and Objects in the same way as the ArrayIterator. Additionally it is possible to iterate over the current iterator entry.
RecursiveIteratorIterator - Can be used to iterate through recursive iterators.
RecursiveIteratorIterator::getInnerIterator: Get inner iterator
you need to make your function recursively
Demo : https://3v4l.org/CR7CD
function searchCategory($categories, $id) {
foreach($categories as $category) {
if($category['id'] == $id)
echo $category['title'] . '<br>';
if(isset($category['children']))
searchCategory($category['children'],$id);
}
}

How to linearize a JSON and make it part of the initial array

I have an array like the following:
array(
'session_id' => 'ea29e7ae5c976794896b4c256f455dd5',
'user_identifier' => "{'user_id':87,'username':'some username','email':'someuseremail.com','first_name':'Some','last_name':'User','company':'Company'}",
'request_uri' => '/'
);
And I would like to convert it to the following:
array(
'session_id' => 'ea29e7ae5c976794896b4c256f455dd5',
'user_id' => 87,
'username' => 'some username',
'email' => 'someuseremail.com',
'first_name' => 'Some',
'last_name' => 'User',
'company' => 'Company',
'request_uri' => '/'
);
Which means I am decoding the JSON at user_identifier key and I am making part of the initial array $original and then I am removing the user_identifier key.
So far this is what I have done:
foreach ($original as $key => $log) {
$original[$key] = (array) $log;
}
foreach ($original as $key => $log) {
foreach($log as $k => $v) {
if ($k === 'user_identifier') {
$original['decoded'] = (array) json_decode($v);
}
}
}
Which is giving me an array like this one:
array(
'session_id' => 'ea29e7ae5c976794896b4c256f455dd5',
'request_uri' => '/',
'user_identifier' => "{'user_id':87,'username':'some username','email':'someuseremail.com','first_name':'Some','last_name':'User','company':'Company'}",
'decoded' => array(
'user_id' => 87,
'username' => 'some username',
'email' => 'someuseremail.com',
'first_name' => 'Some',
'last_name' => 'User',
'company' => 'Company'
)
);
As you may notice this is not even the array I am looking for and I have already one foreach loop to convert the initial result to an array - it's coming as and stdClass object - and then a nested foreach loop for decode the JSON and try to make it part of the initial array.
In such case I will need to add another loop to linearize the array. My concern is this array is just an example but the one I need to convert is a big one.
Is there any better way to achieve this?
I am using PHP 5.3.3
I would do it like this:
Extract the JSON from the original into an array. (Be sure to set the second argument of json_decode so you end up with an array instead of an object.)
$identifier = json_decode($your_array['user_identifier'], true);
merge the extracted array with the original.
$your_array = array_merge($your_array, $identifier);
Unset the now-redundant JSON
unset($your_array['user_identifier']);

PHP - Set array key only if the value is not null

Is there a shorter solution for something like this?
$manufacturer = array();
if(!is_null($params->get('name'))){
$manufacturer['name'] = $params->get('name');
}
if(!is_null($params->get('link'))){
$manufacturer['link'] = $params->get('link');
}
if(!is_null($params->get('description'))){
$manufacturer['description'] = $params->get('description');
}
...
So a key of an array should only be set with the value if the value is not null. This is a bit shorter, but with this solution the keys will exist with the value NULL. But they should not even exist when the value was NULL:
$manufacturer = array(
'name' => !is_null($params->get('name')) ? $params->get('name') : null,
'link' => !is_null($params->get('link')) ? $params->get('link') : null,
'description' => !is_null($params->get('description')) ? $params->get('description') : null
);
EDIT:
It should work for multidimensional arrays and the array keys and param keys may vary
for #u_mulder foreach and #Nono array_filter solutions they work only for simple array, they do not remove null values from multidimensional arrays,
try this recursive function:
<?php
/**
just keep your array like this:
$manufacturer = array(
'name' => $params->get('name'),
'link' => $params->get('link'),
'description' => $params->get('description'),
'attribute' => array (
'street' => $params->get('street'),
...
)
...
);
**/
$manufacturer = [
'name' => 'yoeunes',
'link' => null,
'description' => 'fake description',
'attribute' => [
'street' => null,
'city' => 'Marrakech',
],
];
function array_remove_null($array)
{
foreach ($array as $key => $value) {
if (is_array($value)) {
$array[$key] = array_remove_null($array[$key]);
}
if (is_null($array[$key])) {
unset($array[$key]);
}
}
return $array;
}
echo "<pre>";
print_r(array_remove_null($manufacturer));
output:
Array
(
[name] => yoeunes
[description] => fake description
[attribute] => Array
(
[city] => Marrakech
)
)
$keys = ['name', 'link', ....];
foreach ($keys as $key) {
if(!is_null($params->get($key))){
$manufacturer[$key] = $params->get($key);
}
}
The foreach solutions are possible, but since the array key and params key may vary and because I have another array with values within this array, this is maybe a better solution, or what do you think?
$manufacturer = array(
'name' => !is_null($params->get('name')) ? $params->get('name') : false,
'link' => !is_null($params->get('link')) ? $params->get('link') : false,
'description' => !is_null($params->get('description')) ? $params->get('description') : false,
'attribute' => array (
'street' => !is_null($params->get('street')) ? $params->get('street') : false,
...
)
...
);
$manufacturer = array_filter($manufacturer);
array_filter (without callback) will remove all keys with false. (It's possible as long I don't have boolean values for my keys. If so you can do the same with NULL and then use a callback for array_filter)

Semi-advanced PHP array

So I'm new to Arrays, but I think this should be fairly easy, I just can't wrap my head around it.
I've got an array, that can have a varying amount of keys in it, based on how much input is given by the user.
$array = MY_Class( array(
'type' => 'representatives',
'show_this_many' => '10'
));
easy enough, right?
but I've got 1-4 more keys that could be in it, based on user input. They fill out a form on the first page, and it submits to the second page (which contains the array above).
I need to grab City, State, First, last, based on how many fields the user fills out on the previous page. I can't have blank ones so
$array = MY_Class( array(
'type' => 'representatives',
'show_this_many' => '10',
'city' => '',
'state' => '',
'first' => $_GET['first']
));
won't really work. I need a way to determine which fields have been submitted (preferrably via GET) and build the array that way. so can end up with
$array = MY_Class( array(
'type' => 'representatives',
'show_this_many' => '10',
'state' => $_GET['state'],
'first' => $_GET['first']
));
because state and first had a value while city and last did not.
The first thing that came to mind was something like
$array = MY_Class( array(
'type' => 'representatives',
'show_this_many' => '10',
$constants => $variables
));
//where
$constants = array( //_GET stuff values );
$variables = array( //_GET stuff with values );
// magic method to make it like
// CONSTANTS[0] => VARIABLES[0];
// CONSTANTS[1] => VARIABLES[1];
// so everything is lined up
but I'm not sure how to do it :/
You will want to use a whitelist of possible keys from $_GET so your array doesn't get polluted with spurious (or possibly malicious) keys, and then you can simply append them onto your array with a loop over $_GET.
// Your array is already initialized with some values:
$array = array(
'type' => 'representatives',
'show_this_many' => '10'
);
// Allowed GET keys
$allowed = array('city','state','first');
// Loop over get and add the keys (if allowed)
foreach ($_GET as $key => $value) {
// If the key is allowed and the value isn't blank...
if (in_array($allowed, $key) && !empty($value)) {
$array[$key] = $value;
}
}
// See all the newly added keys...
var_dump($array);
Another option is to just add all the keys to the array, then call array_filter() to remove the blanks.
$array = array(
'type' => 'representatives',
'show_this_many' => '10',
'city' => '',
'state' => '',
'first' => ''
);
// Add values from $_GET, then filter it
$array = array_filter($array, function($val) {
return $val !== '' && !is_null($val);
});
Try the below code
$array = array(
'type' => 'representatives',
'show_this_many' => '10'
);
$fields = array ('city', 'state', 'first');
foreach ($fields as $field) {
if (!empty($_GET[$field])) {
$array[$field] = $_GET[$field]
}
}

multidimensional array - adding a key and value where a key and value is matched

I'm trying to add a key and value (associative) from an array to another array, where one specific key and value match. Here are the two arrays:
$array1 = array(
1 => array(
'walgreens' => 'location',
'apples' => 'product1',
'oranges' => 'product2'
),
2 => array(
'walmart' => 'location',
'apples' => 'product1',
'oranges' => 'product2',
'milk' => 'product3'
)
);
$array2 = array(
1 => array(
'walgreens' => 'location',
'apples' => 'product1',
'oranges' => 'product2',
'bananas' => 'product3',
)
);
Here is the attempt I made at modifying $array1 to have key 'bananas' and value 'product3':
$dataCJ = getCJItem($isbn);
foreach ($array1 as $subKey => $subArray) {
foreach($subArray as $dkey => $dval){
foreach($array2 as $cjk => $cjv){
foreach($cjv as $cjkey => $cjval){
if($dval['walgreens'] == $cjval['walgreens']){
$dval['bananas'] = $cjval['bananas'];
}
}
}
}
}
This doesn't work. How can I fix this?
Change => $dval to => &$dval. Currently you are creating and writing to a new variable and the update will not work in-place.
I would look at array_merge() function!
Here is a start with the PHP doc.
For your specific case, you could do the following :
foreach($array1 as $key1 => $values1){
foreach($array2 as $key2 => $values2){
if($values1[0] == $values2[0]){
$array1[$key1] = array_merge($values1, $values2);
}
}
}
Note to simplify the problem you should inverse the first key=> value pair of the array.
Having an array this way would be a lot simper :
array(
'location' => "The location (eg:walgreens)",
//...
);
This way you could change the comparison to the following instead :
$values1['location'] == $values2['location']
Which would be safer in the case the array is not built with the location as the first pair.

Categories