Organize multidimensional array using Recursion - php

I have an array that I've build dynamically. It has many nested arrays because of the way it's built, but the depth is useless to me, so I organize it right afterwards. It could look like this:
Array
(
[0] => Array
(
[0] => Array
(
[0] => Array
(
[0] => Array
(
[index] => -1
[cost] => 0.189956571618
)
)
)
)
[1] => Array
(
[index] => -1
[cost] => 2.18650011647
)
)
I want to almost-flatten this array (i.e. access its data using $array[$i]['cost'] on all entries, regardless if they were nested deep before I processed them). So far I've been using SPL recursion, with something along these lines:
function flatten($array) {
$return = array();
$it = new RecursiveIteratorIterator(new ParentIterator(new RecursiveArrayIterator($array)), RecursiveIteratorIterator::SELF_FIRST);
foreach($it as $value) {
if(isset($value['cost'])) {
$return[] = $value;
}
}
return $return;
}
It works for the most part, but some of the values in the original array, which do have a 'cost' index in them, fail to be added to the new array because they are passed as nested arrays themselves, like so:
Array
(
[0] => Array
(
[index] => -1
[cost] => 0.189956571618
)
[1] => Array
(
[index] => -1
[cost] => 2.18650011647
)
)
...instead of just (which most of the time I get):
Array
(
[index] => -1
[cost] => 0.189956571618
)
I thought the whole point of using a RecursiveIterator was to go deep within the array and fetch the entries which don't have arrays within them (i.e. the 'values' I want). Am I using the wrong tools for this job? If so, what would be more appropriate to loop through an array for which I don't know the depth? If SPL is the way to go, what am I doing wrong?
Thanks in advance.

you could use array_walk_recursive
EDIT
function flatten($array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) {
if(isset($value['cost']) $return[] = $value;
});
return $return;
}
on the SPL part EDIT long discussion in the chatroom, check OP's answer

Related

Problems changing values in an array/object nested combination array

I don't know what to do to get this done what would like to do. I tried multiple approaches, e.g. I used array_map, array_walk, nested foreach loops with get_object_vars and I worked with json_decode/encode and so on. I always come a little bit further but never reach my goal and I would like to get some guidance from you
Basically when you see the array below, how would you proceed when you want to change some value in the path array for multiple values in the array itself?
My questions:
1) Is it right that I must convert both nested objects to an array first or is this not nesessary to do this? I mean I always get some type conversion error which tells me that I either have everything as an object or array. Is this right?
2) If this mistery is solved, which php array function is the appropriate one to change values in an array(/object)? As I have written above, I tried so many and I don't see the trees in the woods anymore. Which one do you suggest to me to use in a foreach loop?
Array
(
[0] => stdClass Object
(
[doc] => stdClass Object
(
[path] => Array
(
[0] => Bob
[1] => pictures
[2] => food
)
)
)
[1] => stdClass Object
(
[doc] => stdClass Object
(
[path] => Array
(
[0] => Alice
[1] => pictures
[2] => vacations
[3] => rome
)
)
)
)
I would suggest that,
you create an array with keys as new path and value as old path (
path to be replaced).
Loop you path array and check if it is available in above defined array.
If available replace it with key of above defined array.
For example
// array defined as point 1
$change_path_array= array('pics'=>'pictures','meal'=>'food');
// $array is your array.
foreach ($array as $value) {
// loop you path array
for($i=0;$i<count($value->doc->path);$i++){
// check if the value is in defined array
if(in_array($value->doc->path[$i],$change_path_array)){
// get the key and replace it.
$value->doc->path[$i] = array_search($value->doc->path[$i], $change_path_array);
}
}
}
Out Put: picture is replaced with pics and food with meal
Array
(
[0] => stdClass Object
(
[doc] => stdClass Object
(
[path] => Array
(
[0] => Bob
[1] => pics
[2] => meal
)
)
)
[1] => stdClass Object
(
[doc] => stdClass Object
(
[path] => Array
(
[0] => Alice
[1] => pics
[2] => vacations
[3] => rome
)
)
)
)
You can modify the code to check casesensitive.
Example of changing all pictures to photos:
$doc1 = new \stdClass;
$doc1->doc = new \stdClass;
$doc1->doc->path = array('Bob', 'pictures', 'food');
$doc2 = new \stdClass;
$doc2->doc = new \stdClass;
$doc2->doc->path = array('Alice', 'pictures', 'vacations', 'rome');
$documents = array($doc1, $doc2);
/* change all 'pictures' to 'photos' */
foreach ($documents as &$doc) {
foreach ($doc->doc->path as &$element) {
if ($element == 'pictures') {
$element = 'photos';
}
unset($element);
}
unset($doc);
}
print_r($documents);
You can do it like this:
for($i = 0; $i < count($arr); $i++){
$path_array = $arr[$i]->doc->path;
// do your modifications for [i]th path element
// in your case replace all 'Bob's with 'Joe's
$path_array = array_map(function($paths){
if($paths == 'Bob') return 'Joe';
return $paths;
}, $paths_array);
$arr[$i]->doc->path = $path_array;
}

Recursively loop through php array and convert object elements to array storing their class names

I've been hours trying to figure this, i know i'm missing something obvious. This is the problem:
I have an array that some of its elements are objects, others are arrays and others are of other types. What i need is:
Loop through the array and convert the object elements into array elements.
Loop recursively (or whatever you want to call it) through these new array elements (the ones that were converted from objects) and the elements that already are array and perform the previous task (that is: convert the object elements into array elements).
Here's the gotcha: every time an object element is converted into array, the class name of the object has to be added as the first element of the array that is generated by converting the object.
Here is a simplified example:
Array:
Array
(
[0] => PhpParser\Node\Expr\Assign Object
(
[var] => PhpParser\Node\Expr\Variable Object
(
[name] => bar
)
[expr] => PhpParser\Node\Scalar\LNumber Object
(
[value] => 22
)
)
)
I need a function like this:
//$arr is the array previously posted
$arr = cool_object_to_array($arr);
var_dump($arr );
outputs
Array
(
[0] => Array
(
[0] => PhpParser\Node\Expr\Assign
[var] => Array
(
[0] => PhpParser\Node\Expr\Variable
[name] => bar
)
[expr] =>Array
(
[0] => PhpParser\Node\Scalar\LNumber
[value] => 22
)
)
)
The nesting level is unknown. It can be many arrays nested on objects nested on other objects or arrays, etc. The example is just very simplified. I need a solution that handles that too.
Thanks in advance for all your answers!
This should do it:
function cool_object_to_array($array) {
foreach ($array as $key => &$value) {
if (is_object($value)) {
$type = get_class($value);
$value = (array) $value;
array_unshift($value, $type);
}
if (is_array($value)) {
$value = cool_object_to_array($value);
}
}
return $array;
}

Anonymous PHP function with in_array not working

I have this simple array $tree in PHP that I need to filter based on an array of tags matching those in the array.
Array
(
[0] => stdClass Object
(
[name] => Introduction
[id] => 798162f0-d779-46b6-96cb-ede246bf4f3f
[tags] => Array
(
[0] => client corp
[1] => version 2
)
)
[1] => stdClass Object
(
[name] => Chapter one
[id] => 761e1909-34b3-4733-aab6-ebef26d3fcb9
[tags] => Array
(
[0] => pro feature
)
)
)
I tried using an anonymous function like so:
$selectedTree = array_filter($tree, function($array) use ($selectedTags){
return in_array($array->tags, $selectedTags, true);
});
$selectedTags:
Array
(
[0] => client corp
)
The above is returning empty when I'd expect item 1 to be returned. No error thrown. What am I missing?
In case of in_array($neddle, $haystack). the $neddle must need to be a String, but you're giving an array that is why its not behaving properly.
But if you like to pass array as value of $selectedTags then you might try something like below:
$selectedTree = array_filter($tree, function($array) use ($selectedTags){
return count(array_intersect($array->tags, $selectedTags)) > 0;
});
Ref: array_intersect
If I am reading the question correctly, you need to look at each object in $tree array and see if the tags property contains any of the the elements in $selectedTags
Here is a procedural way to do it.
$filtered = array();
foreach ($tree as $key => $obj) {
$commonElements = array_intersect($selectedTags, $obj->tags);
if (count($commonElements) > 0) {
$filtered[$key] = $obj;
}
}
I was going to also post the functional way of doing this but, see thecodeparadox's answer for that implementation.

Renaming the keys in multidimensional associate arrays

I have searched SO and Google and have found lots of similar questions, but nothing that fits my exact use case.
I have an array of arrays like this:
Array
(
[0] => Array
(
[id] => c80c5133-1140-8187-ad3b-524b4ed0f1a8
[date_entered] => 10/01/2013 03:38pm
)
[1] => Array
(
[id] => 176815c6-b57f-7643-0f08-524b4f22b51c
[date_entered] => 10/01/2013 03:42pm
)
[2] => Array
(
[id] => df0f8824-0b12-b92e-1d2e-524c6cb19c41
[date_entered] => 10/02/2013 11:56am
)
)
I need to rename the keys of the first dimension to be the value of the date_entered key in the second dimension arrays like this so that I can (hopefully) sort the array by the most recent date. I need to preserve the contents of each array because I will need to grab the ID that corresponds to the correct date.
Array
(
[10/01/2013 03:38pm] => Array
(
[id] => c80c5133-1140-8187-ad3b-524b4ed0f1a8
[date_entered] => 10/01/2013 03:38pm
)
[10/01/2013 03:42pm] => Array
(
[id] => 176815c6-b57f-7643-0f08-524b4f22b51c
[date_entered] => 10/01/2013 03:42pm
)
[10/02/2013 11:56am] => Array
(
[id] => df0f8824-0b12-b92e-1d2e-524c6cb19c41
[date_entered] => 10/02/2013 11:56am
)
)
I am trying to do it like this (which is obviously not correct) but for the life of me I still can't get it.
foreach ($array as $key) {
foreach ($key as $subkey => $subvalue) {
if ($subkey == 'date_entered') {
// change the name of the key?
}
}
}
I am really struggling with multidimensional arrays and manipulating them, no matter how much I read and practice! Can anyone help?
This code should do it:
$newArray = array();
foreach ($array as $id => $dataset) {
$newArray[ $dataset['date_entered'] ] = $dataset;
}
I created a new array here because "changing the array within a foreach loop may lead to unexpected behaviour" (source).
If you really need to preserve your original array, you can use your numeric indices for accessing the elements:
$arrCount = count($array);
for ($i=0; $i<$arrCount; $i++) {
$array[ $dataset['date_entered'] ] = $array[$i];
unset($array[$i]);
}
All elements get copied before they get unset/deleted at the previous key.

I want to add sub arrays to one single array keep id and value in php

My input array :
Array
(
[0] => Array
(
[id] => 1
[status_name] => Released
)
[1] => Array
(
[id] => 2
[status_name] => Under Construction
)
)
I want the output result :
Array (
[1] => Released
[2] => Under Construction
)
USe sub array id as output array key value and status_name as value array.
This is built into php as array_column. You would have:
$status_names = array_column($data, 'status_name', 'id');
print_r($status_name);
Bonus points on question as I had no idea this existed until looking for an answer for you.
Try the following:
function reOrderArray($input_array)
{
$result = array();
foreach ($input_array as $sub_array)
{
$result[$sub_array['id']] = $sub_array['status_name'];
}
return $result;
}
There might be a built-in php function to do this, array functions in php are quite powerful. I am, however, woefully unaware of one.

Categories