I may not word this issue properly, so here's what am trying to achieve.
array {
[0]=> {
["Abilities"]=> { ["Numerical"]=> 3 }
}
[1]=> {
["Abilities"]=> { ["Verbal"]=> 1 }
}
[2]=> {
["Domain"]=> { ["Programming"]=> 0 }
}
}
to
array {
[0]=> {
["Abilities"]=> { ["Numerical"]=> 3 ["Verbal"]=> 1 }
}
[1]=> {
["Domain"]=> { ["Programming"]=> 0 }
}
}
I get this array from an external source so I need optimized this way to use it.
The array you're getting from an external source is like a set of separate branches you need to merge into a single tree. You can use a recursive function to create the "optimized" structure you're going for. A recursive approach should work regardless of the depth of each branch.
function merge_branches(array $branches): array
{
$merge = function ($node, &$tree) use (&$merge) {
if (is_array($value = reset($node))) {
$merge($value, $tree[key($node)]); // merge branch node recursively
} else {
$tree[key($node)] = $value; // set leaf node to value
}
};
$tree = [];
foreach ($branches as $branch) {
$merge($branch, $tree);
}
return $tree;
}
$optimized = merge_branches($external);
Related
I am trying to iterate over a php object and changing each string value by reference, but something is just not working. In some arrays the strings will not be changed. Anyone has an idea why? Or has a suggestion on how to solve the task?
Here is my code:
recursive_object_string_changer($object);
function recursive_object_string_changer($object)
{
if($object == null) {
return;
}
foreach ($object as &$attribute) {
if (is_string($attribute)) {
$attribute = $attribute."!";
} else if (is_array($attribute)) {
recursive_object_string_changer($attribute);
} else if (is_object($attribute)) {
recursive_object_string_changer($attribute);
}
}
unset($attribute);
}
Thank you very much!
I think you want to make the function's signature also accept the initial object as a reference so that the recursion works on subsequent calls.
recursive_object_string_changer($object);
function recursive_object_string_changer(&$object)
{
if ($object === null) {
return;
}
foreach ($object as &$attribute) {
if (is_string($attribute)) {
$attribute .= "!";
} elseif (is_array($attribute)) {
recursive_object_string_changer($attribute);
} elseif (is_object($attribute)) {
recursive_object_string_changer($attribute);
}
}
unset($attribute);
}
I used this for a sample:
$object = new stdClass();
$object->string = 'Test';
$object->array = [
'a',
'b',
'c',
];
$subObject = new stdClass();
$subObject->string = 'Another String';
$object->object = $subObject;
Which produces:
object(stdClass)#1 (3) {
["string"]=>
string(5) "Test!"
["array"]=>
array(3) {
[0]=>
string(2) "a!"
[1]=>
string(2) "b!"
[2]=>
string(2) "c!"
}
["object"]=>
object(stdClass)#2 (1) {
["string"]=>
string(15) "Another String!"
}
}
You might always want to add a guard before the for loop to make sure that $object is an array or an object in the first place.
I have the following code but I am wondering how I can make it more efficient.
if ($genres){
$arr = array();
foreach ($genres as $i) {
$arr[] = $i->name;
}
$genres_arr = $arr;
}
if ($themes){
$arr = array();
foreach ($themes as $i) {
$arr[] = $i->name;
}
$themes_arr = $arr;
}
var_dump($genres_arr);
var_dump($themes_arr);
I've tried putting them into an if statement but because they both always exists only the first one runs. I want to check to see if both exist and always run them both through a foreach loop. If only one exists I want only the one to run.
These are the array structures.
["genres"]=>
array(1) {
[0]=>
object(stdClass)#1579 (2) {
["id"]=>
int(25)
["name"]=>
string(26) "Hack and slash/Beat 'em up"
}
}
["themes"]=>
array(3) {
[0]=>
object(stdClass)#1576 (2) {
["id"]=>
int(1)
["name"]=>
string(6) "Action"
}
}
I want to have them as flattered as at the moment they are inside objects. I am then going to implode them into a list for WordPress use.
This code works but its repetitive and some help would be great!
I think you can use array column because it can read values from "A multi-dimensional array or an array of objects from which to pull a column of values from" like this:
if ($genres) {
$genres_arr = array_column($genres, 'name');
}
if ($themes) {
$themes_arr = array_column($themes, 'name');
}
var_dump($genres_arr);
var_dump($themes_arr);
In this way the easiest simplification would be to introduce a new function, which builds the array.
function getNames($arr) {
if (!is_array($arra)) return false;
return array_map(function($item) {
return $item->name;
}, $arr);
}
$themes_arr = getNames($themes);
$genre_arr = getNames($genres);
This is how I would combine them
$sets = [];
if ($genres){
$sets['genres'] = $genres;
}
if ($themes){
$sets['themes'] = $themes;
}
$arr = array();
foreach( $sets as $type => $data ){
foreach ($genres as $i) {
$arr[$type][] = $i->name;
}
}
claudio's answer is perfect for this situation, but in the more general case you can also define a mapping function, which you then use with array_map for both sets of objects, e.g.
$mapper = function ($item) { return $item->name; };
$genres_arr = array_map($mapper, $genres);
$themes_arr = array_map($mapper, $themes);
This has the advantage of being able to run more complex logic (using a getter function instead of direct property access, etc), if you need it in future.
Ideally, both objects would implement a common interface, so that it was clear exactly what kinds of objects the mapping function was designed for.
So I got a simple function that works, but I'm trying to evolve my experince wtih OOP and try to make sure I can use my code without having to edit everything.
Here is my simple function
$xmlfeed = file_get_contents('/forum/syndication.php?limit=3');
$xml = new SimpleXMLElement($xmlfeed);
$result = $xml->xpath('channel/item/title');
while(list( , $node) = each($result)) {
echo $node;
}
Now so far I got to this point:
class ForumFeed {
private function getXMLFeeds($feed = 'all'){
/*
Fetch the XML feeds
*/
$globalFeedXML = file_get_contents('/forum/syndication.php?limit=3');
$newsFeedXML = file_get_contents('/forum/syndication.php?fid=4&limit=3');
/*
Turn feed strings into actual objects
*/
$globalFeed = new SimpleXMLElement($globalFeedXML);
$newsFeed = new SimpleXMLElement($newsFeedXML);
/*
Return requested feed
*/
if ($feed == 'news') {
return $newsFeed;
} else if ($feed == 'all') {
return $globalFeed;
} else {
return false;
}
}
public function formatFeeds($feed) {
/*
Format Feeds for displayable content..
For now we're only interested in the titles of each feed
*/
$getFeed = $this->getXMLFeeds($feed);
return $getFeed->xpath('channel/item/title');
}
}
$feeds = new ForumFeed();
However when trying to echo $feeds->formatFeeds('all'); it doesn't return anything. The results is blank.
What am I doing wrong?
var_dump($feeds->formatFeeds('all')); returns
array(3) {
[0]=>
object(SimpleXMLElement)#3 (0) {
}
[1]=>
object(SimpleXMLElement)#4 (0) {
}
[2]=>
object(SimpleXMLElement)#5 (0) {
}
}
According to PHPs documentation SimpleXMLElement::xpath returns an array of SimpleXMLElements or false on error. Maybe var_dump($feeds->formatFeeds('all')); prints something you then can use to debug.
Edit: The XPath query returns results, so probably there is a logical error in your query or the returned elements don't have content.
As title, I did it like below:
$array=array(0,1,2,3);
$result=array();
function makeArray($array,$result,$value){
$str='$result';
for ($i=0;$i<count($array);$i++){
$str.='['.$i.']';
}
$str.='="'.$value.'";';
eval($str);
return $result;
}
It can realize result when param $result is an empty array,but It report an error when $result is an array.
Error like :
Cannot use a scalar value as an array.
Anyways can realize it?
Thanks first!
Use pass by reference, not eval:
function makeArray($indexes, &$result, $value) {
$here =& $result;
foreach ($indexes as $i) {
if (!(isset($here[$i]) && is_array($here[$i]))) {
$here[$i] = array();
}
$here =& $here[$i];
}
$here = $value;
}
$array=array(0,1,2,3);
$result=array();
makeArray($array, $result, 3);
var_dump($result);
Output:
array(1) {
[0]=>
array(1) {
[1]=>
array(1) {
[2]=>
array(1) {
[3]=>
int(3)
}
}
}
}
Putting & before a function parameter means it will be passed by reference, so modifications to the variable inside the function will affect the original variable that was passed. And using =& in an assignment assigns a reference, so the target variable is an alias for the source.
This question already has answers here:
Convert a PHP object to an associative array
(33 answers)
Closed 6 months ago.
I'm using amazon product advertising api. Values are returned as a multidimensional objects.
It looks like this:
object(AmazonProduct_Result)#222 (5) {
["_code":protected]=>
int(200)
["_data":protected]=>
string(16538)
array(2) {
["IsValid"]=>
string(4) "True"
["Items"]=>
array(1) {
[0]=>
object(AmazonProduct_Item)#19 (1) {
["_values":protected]=>
array(11) {
["ASIN"]=>
string(10) "B005HNF01O"
["ParentASIN"]=>
string(10) "B008RKEIZ8"
["DetailPageURL"]=>
string(120) "http://www.amazon.com/Case-Logic-TBC-302-FFP-Compact/dp/B005HNF01O?SubscriptionId=AKIAJNFRQCIJLTY6LDTA&tag=*********-20"
["ItemLinks"]=>
array(7) {
[0]=>
object(AmazonProduct_ItemLink)#18 (1) {
["_values":protected]=>
array(2) {
["Description"]=>
string(17) "Technical Details"
["URL"]=>
string(217) "http://www.amazon.com/Case-Logic-TBC-302-FFP-Compact/dp/tech-data/B005HNF01O%3FSubscriptionId%3DAKIAJNFRQCIJLTY6LDTA%26tag%*******-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB005HNF01O"
}
}
[1]=>
object(AmazonProduct_ItemLink)#17 (1) {
["_values":protected]=>
array(2) {
I mean it also has array inside objects. I would like to convert all of them into a multidimensional array.
I know this is old but you could try the following piece of code:
$array = json_decode(json_encode($object), true);
where $object is the response of the API.
You can use recursive function like below:
function object_to_array($obj, &$arr)
{
if (!is_object($obj) && !is_array($obj))
{
$arr = $obj;
return $arr;
}
foreach ($obj as $key => $value)
{
if (!empty($value))
{
$arr[$key] = array();
objToArray($value, $arr[$key]);
}
else {$arr[$key] = $value;}
}
return $arr;
}
function convertObjectToArray($data) {
if (is_object($data)) {
$data = get_object_vars($data);
}
if (is_array($data)) {
return array_map(__FUNCTION__, $data);
}
return $data;
}
Credit to Kevin Op den Kamp.
I wrote a function that does the job, and also converts all json strings to arrays too. This works pretty fine for me.
function is_json($string) {
// php 5.3 or newer needed;
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
function objectToArray($objectOrArray) {
// if is_json -> decode :
if (is_string($objectOrArray) && is_json($objectOrArray)) $objectOrArray = json_decode($objectOrArray);
// if object -> convert to array :
if (is_object($objectOrArray)) $objectOrArray = (array) $objectOrArray;
// if not array -> just return it (probably string or number) :
if (!is_array($objectOrArray)) return $objectOrArray;
// if empty array -> return [] :
if (count($objectOrArray) == 0) return [];
// repeat tasks for each item :
$output = [];
foreach ($objectOrArray as $key => $o_a) {
$output[$key] = objectToArray($o_a);
}
return $output;
}
This is an old question, but I recently ran into this and came up with my own solution.
array_walk_recursive($array, function(&$item){
if(is_object($item)) $item = (array)$item;
});
Now if $array is an object itself you can just cast it to an array before putting it in array_walk_recursive:
$array = (array)$object;
array_walk_recursive($array, function(&$item){
if(is_object($item)) $item = (array)$item;
});
And the mini-example:
array_walk_recursive($array,function(&$item){if(is_object($item))$item=(array)$item;});
In my case I had an array of stdClass objects from a 3rd party source that had a field/property whose value I needed to use as a reference to find its containing stdClass so I could access other data in that element. Basically comparing nested keys in 2 data sets.
I have to do this many times, so I didn't want to foreach over it for each item I need to find. The solution to that issue is usually array_column, but that doesn't work on objects. So I did the above first.
Just in case you came here as I did and didn't find the right answer for your situation, this modified version of one of the previous answers is what ended up working for me:
protected function objToArray($obj)
{
// Not an object or array
if (!is_object($obj) && !is_array($obj)) {
return $obj;
}
// Parse array
foreach ($obj as $key => $value) {
$arr[$key] = $this->objToArray($value);
}
// Return parsed array
return $arr;
}
The original value is a JSON string. The method call looks like this:
$array = $this->objToArray(json_decode($json, true));