I have a multidimensional array in PHP and want to be able to search through it and find all values that are objects.
The reason I want to do this is so that when an object is found I can replace it with an array by calling an output() method on it. The output() method uses get_object_vars() to turn itself into an array, which it then returns.
Here's an example which achieves what I want manually (but only with 2 levels of depth):
// First level search...
foreach($array as $k => $v) {
// Check if it's an array.
if (is_array($v)) {
// Second level search...
foreach($v as $k2 => $v2) {
// If it's an object - convert it!
if (is_object($v2)) {
$array[$k][$k2] = $array[$k][$k2]->output();
}
}
}
// If it's an object - convert it!
if (is_object($v)) {
$array[$k] = $array[$k]->output();
}
}
Tim Cooper's answer is wrong because the function must have a parameter that is passed by reference and not by value.
php > class Foo { public function output() { return "this was an object"; } }
php > $a = array( 1 => array( 2 => array( 'foo', 'red', 1, new Foo() ) ) );
php > array_walk_recursive( $a, function( $item, $key ) {
if ( is_object( $item ) ) {
$item = $item->output();
}
} );
php > print_r( $a );
Array
(
[1] => Array
(
[2] => Array
(
[0] => foo
[1] => red
[2] => 1
[3] => Foo Object
(
)
)
)
)
Versus passing by reference:
php > array_walk_recursive( $a, function( &$item, $key ) {
if ( is_object( $item ) ) {
$item = $item->output();
}
} );
php > print_r( $a );
Array
(
[1] => Array
(
[2] => Array
(
[0] => foo
[1] => red
[2] => 1
[3] => this was an object
)
)
)
You just need a recursive function:
function objects_to_arrays_recursive (&$array) {
foreach ($array as &$member) {
if (is_object($member)) {
$member = $member->output();
}
if (is_array($member)) {
objects_to_arrays_recursive($member);
}
}
}
This will call the output() method of every object and store the result in the key that originally held the object.
Caveats:
This will loop the objects once they have been converted and also convert child objects. You may not want to do this, particularly if you have circular references as this would result in an infinte loop. It can be avoided just by changing the 2 ifs into if / elseif.
This does not check whether a given object has an output() method to call. You should probably add an is_a()/instanceof check.
This function takes its argument by reference, which means the input array will be modified. If you need to keep the original array intact, you will need to make a copy of it first.
Related
Below is my code that output this array:
// update users
$where_in = array('1102','');
$admin_data = $this->db->select('id,email,domain')->where_in('id',$where_in)->get('users')->result();
echo "<pre>";print_r($admin_data);
current output array
1102,
Array
(
[0] => stdClass Object
(
[id] => 1
)
)
1111,
Array
(
[0] => stdClass Object
(
[id] => 1132
)
[1] => stdClass Object
(
[id] => 1133
)
)
I am trying to accomplish by doing this, but not getting expected result.
foreach ($admin_data as $key) {
if (count($admin_data) < 2) {
unset($admin_data);
}
}
Expected result: I want to remove whole array element, where array key less than 2. I wish to get only array with more than 1 key count like below:
1111,
Array
(
[0] => stdClass Object
(
[id] => 1132
)
[1] => stdClass Object
(
[id] => 1133
)
)
I believe you are trying to unset elements within your "$admin_data" array, however you call unset on the array itself. If you pass the index of the array element into your loop, with the element itself, then you can check if the element has less than two inner elements, and unset that element accordingly.
$elementsToRemove = [];
foreach($admin_data as $index => $key) {
if (count($key) < 2) {
$elementsToRemove[] = $index;
}
}
$newArray = array_diff_key($admin_data, array_flip($elementsToRemove));
I'm not sure if I understood, but if you have an array of arrays and want to remove the inner arrays with less than 2 elements, you could do:
<?php
$original = [
["a"],
["b", "c"],
];
$filtered = array_filter(
$original,
function ($innerArray) {
return count($innerArray) >= 2;
}
);
print_r($filtered);
The result is
Array
(
[1] => Array
(
[0] => b
[1] => c
)
)
PHP Fiddle
Check again your code, it contains some logical problems:
foreach ($admin_data as $key) {
if (count($admin_data) < 2) {
unset($admin_data);
}
}
If $admin_data has one item, the if (count($admin_data) < 2) test is true and
unset($admin_data); is executed. That means (simplifying) that the all the contents of $admin_data are deleted.
If $admin_data has two or more items, the if (count($admin_data) < 2) test is never true. And it is so for each iteration.
Both cases are not logical, because you do not need the foreach loop for this test to work. Besides, you might not want to delete $admin_data contents.
What you need to do, is to iterate through $admin_data items, check the item to see if it is an array and if that array has one item, remove the item from the $admin_data array. Or you could create another array only with the valid items.
Here is a possible example of both:
/* unset the item if it is an array with one item */
foreach ($admin_data as $key => $value) {
if (is_array($value) && count($value) === 1) {
unset($admin_data[$key]);
}
}
/* create a new array with the valid items */
$valid_admin_data = [];
foreach ($admin_data as $key => $value) {
if (is_array($value) && count($value) > 1) {
$valid_admin_data[$key] = $value;
}
}
I would also suggest you to read again the foreach documentation. Also, be consistents with names of variables, since it helps avoid misunderstandings: for instance, foreach ($admin_data as $key) could be better named to foreach ($admin_data as $value), since with one variable, you extract the value of current item.
I'm trying to read recursively into an array until I'm getting a string. Then I try to explode it and return the newly created array. However, for some reason it does not assign the array:
function go_in($arr) { // $arr is a multi-dimensional array
if (is_array($arr))
foreach($arr as & $a)
$a = go_in($a);
else
return explode("\n", $arr);
}
EDIT:
Here's the array definition as printed by print_r:
Array
(
[products] => Array
(
[name] => Arduino Nano Version 3.0 mit ATMEGA328P
[id] => 10005
)
[listings] => Array
(
[category] =>
[title] => This is the first line
This is the second line
[subtitle] => This is the first subtitle
This is the second subtitle
[price] => 24.95
[quantity] =>
[stock] =>
[shipping_method] => Slow and cheap
[condition] => New
[defects] =>
)
[table_count] => 2
[tables] => Array
(
[0] => products
[1] => listings
)
)
I'd use this:
array_walk_recursive($array,function(&$value,$key){
$value = explode("\n",$value);
});
However, this fixes your function:
function &go_in(&$arr) { // $arr is a multi-dimensional array
if (is_array($arr)){
foreach($arr as & $a) $a = go_in($a);
} else {
$arr = explode("\n", $arr);
}
return $arr;
}
When writing nested conditions/loops - always add braces for better readability and to prevent bugs.. Also you should return the go_in function, because it is recursive, it needs to be passed to the calling function instance.
function go_in($arr) { // $arr is a multi-dimensional array
if (is_array($arr))
{
foreach($arr as &$a)
{
return go_in($a);
}
}
else
{
return ($arr);
}
}
The original array was not returned in the function:
function go_in($arr) {
if (is_array($arr))
foreach($arr as &$a)
$a = go_in($a);
else
if (strpos($arr, "\n") !== false)
return explode("\n", $arr);
return $arr;
}
EDIT:
Now, it only really edits the strings that contain a linebreak. Before it would edit every string which meant that every string was returned as an array.
I have this array of objects. However I want to only pull out the object which meets a certain criteria. If you look at the "post_title" key, I want to only pull out the object if it does not contain a comma. Is this possible?
So in my example below, I want to pull out the array in index 2 as that is the only one that has no comma in the "post_title" key.
Array
(
[0] => stdClass Object
(
[ID] => 504
[post_title] => Playroom (Black, Custom 2)
)
[1] => stdClass Object
(
[ID] => 503
[post_title] => Playroom (Black, Custom 1)
)
[2] => stdClass Object
(
[ID] => 252
[post_title] => Play (Black)
)
)
1
Thank you for looking at this.
Yes you can but you will have to code it yourself, like this:
$results = array();
foreach ($source as $obj) {
if (strpos($obj->post_title, ',') === false) {
$results[] = $obj;
}
}
// $results will now have the elements filtered
check array_filter function - http://php.net/manual/en/function.array-filter.php
$myArray = /*snip*/;
function titleDoesNotContainComma($object) {
return strpos($object->post_title, ',') === false;
}
$filteredArray = array_filter($myArray, 'titleDoesNotContainComma');
What have you tried? You could just foreach over the array and build a new array with the relevant results. Alternatively you could use array_filter, something like this:
function hasComma( $object ) {
if( strpos( $object->post_title, ',' ) ) {
return false;
} else {
return true;
}
}
var_dump( array_filter( $array, 'hasComma' ) );
I want would like to get the parent node of the array below, but I don't find a way to easily do this.
Normally for getting the id, I would do this in PHP:
echo $output['posts']['11']['id'];
But how can I get to the parent node "11" when I get the value of "id" passed from a $_GET/$_POST/$_REQUEST? (ie. $output['posts']['11'][$_GET[id]])
Array
(
[posts] => Array
(
[11] => Array
(
[id] => 482
[time] => 2011-10-03 11:26:36
[subject] => Test
[body] =>
[page] => Array
(
[id] => 472
[title] => News
)
[picture] => Array
(
[0] => link/32/8/482/0/picture_2.jpg
[1] => link/32/8/482/1/picture_2.jpg
[2] => link/32/8/482/2/picture_2.jpg
[3] => link/32/8/482/3/picture_2.jpg
)
)
)
)
$parent = null;
foreach ($array['posts'] as $key => $node) {
if ($node['id'] == $_GET['id']) {
echo "found node at key $key";
$parent = $node;
break;
}
}
if (!$parent) {
echo 'no such id';
}
Or possibly:
$parent = current(array_filter($array['posts'], function ($i) { return $i['id'] == $_GET['id']; }))
How this should work exactly depends on your array structure though. If you have nested arrays you may need a function that does something like the above recursively.
array_keys($output['posts']);
will give you all keys within the posts array, see http://php.net/manual/en/function.array-keys.php
you could try with something like:
foreach ($posts as $post){
foreach( $items as $item){
if ( $item['id'] == [$_GET[id] ){
// here, the $post is referring the parent of current item
}
}
}
I don't think it is possible while an array of array is not a DOM or any tree structure. In an array you can store any reference. but you can not naturally refer to an array containing a reference.
Check rolfs example for array_filter at php manual http://www.php.net/manual/en/function.array-filter.php#100813
So you could do it like this
$filtered = array_filter($output, function ($element) use ($_GET["id"]) { return ($element["id"] == $_GET["id"]); } );
$parent = array_pop($filtered);
I have a recursion function that parses an object/array with a global variable. If I comment out the global variable I get nothing but if I leave it in it keeps adding to the array other values that should be in it own result set. Do I need to change something here?
UPDATE #2:
How can I get the return I want, I thought I was pushing all unique values to the array?
function getResp($objectPassed) {
foreach($objectPassed as $element) {
if(is_object($element)) {
// recursive call
$in_arr = getResp($element);
}elseif(is_array($element)) {
$in_arr = getResp($element);
} else {
// XML is being passed, need to strip it
$element = strip_tags($element);
// Trim whitespace
$element = trim($element);
// Push to array
if($element != '') {
if (!preg_match("/^[0-9]$/", $element)) {
if (!in_array($element,$in_arr)) {
$in_arr[] = $element;
}
}
}
}
}
return $in_arr;
}
INPUT:
stdClass Object
(
[done] => 1
[queryLocator] =>
[records] => Array
(
[0] => stdClass Object
(
[type] => typeName
[Id] => Array
(
[0] => a0E50000002jxhmEAA
[1] => a0E50000002jxhmEAA
)
)
[1] => stdClass Object
(
[type] => typeName
[Id] => Array
(
[0] => a0E50000002jxYkEAI
[1] => a0E50000002jxYkEAI
)
)
)
[size] => 2
)
RETURN:
Array
(
[0] => a0E50000002jxYkEAI
)
WANTED RETURN:
Array
(
[0] => a0E50000002jxYkEAI
[1] => a0E50000002jxhmEAA
)
Is a global variable necessary? Otherwise you could simplify it this way:
function getResp($objectPassed, &$in_arr = array()) { // <-- note the reference '&'
foreach($objectPassed as $element) {
if(is_object($element) || is_array($element)) { // <-- else if statement simplified
getResp($element,$in_arr);
} else {
// XML is being passed, need to strip it
$element = strip_tags($element);
// Trim whitespace
$element = trim($element);
// Push to array
if($element != '' && // <-- everything in one test
!preg_match("/^[0-9]$/", $element) &&
!in_array($element,$in_arr))
{
$in_arr[] = $element;
}
}
}
return $in_arr;
}
Then you do:
$result = getResp($data);
If a recursive function has to access the same resource over and over again (in this case the initial array), I would always pass this as a reference.
I don't know if is measurable but I would guess that this is much more efficient than copying values.