Array omitting some keys

I’m trying to consume an API delivering an object with a weird aggregation of the data it contains. So this disclaimer is in defence of the nested search you’re about to see. The API itself can be located here. Now the following code

  1. converts the response to an associative array containing some objects
  2. grabs teams from the objects and fetches another set of data (unfortunately, via a fresh API call)

The goal is to flush out some unwanted keys and index the data in a way that is easier for me to access.


$api = new FootballData();
$filter = ['timeFrameStart', 'timeFrameEnd', 'status', 'href', 'matchday', 'date', 'odds'/*, 'count'*/];
$compute = array();
$cascadingStats = array('goalsAwayTeam', 'goalsHomeTeam', 'result', 'halfTime');
$track = 0;
$response = @$api->fetchFixturesFor('n4');


$ironItOut = function ($obj, $index) use ($filter, &$ironItOut, $cascadingStats) {
	foreach ($obj as $key => $value) {
		if (is_object($value)) {
			$ironItOut($value, $index);
		}
		elseif(!in_array($key, $filter)) {
			if (in_array($key, $cascadingStats)) {
				$compute[$index]['cascadingStats'][] = array($key => $value);
			}
			else {
				$compute[$index][$key] = $value; // homeTeamName gets lost here. it's also a key

				//echo("$key<br>"); //it magically appears here but isn't computed
			}
		}
	}
};

try {
	if ($response === FALSE) throw new Exception("Error Processing Request", 1);
	
}
catch (Exception $e) {
	echo $e->getMessage();
}

$start = (int) explode(' ', microtime())[1];

foreach ($response->fixtures as $key => $fixtures) {

	// for each pair, fetch their head to head
	$id = $api->getFixtureId($fixtures->homeTeamName, $fixtures->awayTeamName, 'n4');
	$h2h = $api->headToHead($id);


	foreach($h2h as $key => $stdClass) {
		if (isset($stdClass->count) && $stdClass->count > 2) {

			$track++;
			foreach ($stdClass as $key => $stats) {
				if (is_a($stats, 'stdClass') || is_array($stats)) {
					$compute[$track]['cascadingStats'] = []; // after hashing `cascadingStats` length and `count` should be equal

					$ironItOut($stats, $track);
				}
			 	elseif(!in_array($key, $filter)) {
			 		$compute[$track][$key] = $stats; // filter off undesirable keys
			 	}
			}
		 }
	}
}

var_dump((int) explode(' ', microtime())[1] - $start, $compute);

The current code indexes all the keys as desired but leaves out some particular keys and I cannot figure out why. I need some extra pair of eyes to figure out if something is overwriting them or why they are disappearing. Thank you.

Try this:

$compute[$key][$index] = $value;

Apologies over my inability to reply sooner. I temporarily lost access to my machine. I’ve tried the line you suggested but the result was similar. Those keys are still absent.

Just in case, you need to explore the other class yourself, you can run everything along with this file. Along with the code in the original post. Thank you.

@marklenon95 I eventually got around it by doing this

$ironItOut = function ($obj, $index) use ($filter, &$ironItOut, $cascadingStats, &$compute) {

It appears it wasn’t seeing either the array nor that key cascadingStats. But that wasn’t the case, it was seeing them but the value was disappearing outside the closure. However, useing it reined the values in and now I have an object I can work with. Thanks again for your time.

Is that because you declared the $compute array in the main code, but when you use it inside the ironItOut function, you don’t declare it as global, hence it creates a new local array of the same name, and then forgets it when you leave the scope of the function?

I have a mental block when I see the & character due to previous issues with having to write stuff in C, and I’m unfamiliar with the use keyword. Something for me to read up on, I think.

1 Like

Yea. You raised some interesting points. One of the things PHP haters complain about is the lack of variable initialization. So you can’t easily tell whether you’re using one from the global or local scope. It’s the curse of weak typed languages that objects are created and mutate dynamically. A quick way to confirm what scope was being contacted will probably involve running something like this

$fritter[5]['sitepoint'][] = 'elizabeth';
$nmeri = 'wants her to leave him finally';

$myClosure = function ($argument1, $argument2) {
	// global $fritter, $nmeri;
	$fritter[5]/*['sitepoint'][]*/ = $argument1; // changes either way
	$nmeri = $argument2;
};

$myClosure('I\'m not into you', 'end of the road');
var_dump($fritter, $nmeri);

This quickly exposes the ‘curse’. You could also do something more plain to other developers by using array_key_exists(). Then the code in that block will obviously not run unless you uncomment the line at the top of the function. You can also see global takes infinite parameters.

One difference between use and global is that the former only works on closures, lambdas, anonymous functions etc. global goes anywhere as long as there’s nested scope and the variable we need is truly visible everywhere.

So, I could have used $compute and it would “work” but the problem would persist. Why?

I had a similar argument with someone recently but it was based in Java. Unfortunately I don’t know what the situation is like in C but by default, primitives are passed by value in PHP. Which means, when I throw in $compute in the use invocation, the $compute[$index] value changes during each foreach iteration. That’s what I meant when I said

So by adding this “&” sign, I was in essence passing this ===> $compute directly. Any changes during the iteration gets baked in. It’s like calling response.writeHead when returning server responses for Node, everything is baked in.

The second difference I haven’t mentioned yet is that use passes by value. global neatly handles the reference-vs-value confusion by directly referencing the variable itself. Generally, you need this when you intend changing this object, as opposed to simply reading it.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.