0

I'm working on an application where the user should see a list with charts. The data for the charts should be fetched from a database (which currently counts about 785 rows) and then get sorted to form a valid JSON string. Right not I try to do it like this

while($row = $res->fetch_assoc()) {
    if(count($appData) == 0 ){
        $appData[] = array(
            "name" => $row["name"],
            "date" => array($row["date"]),
            "android" => array($row["android_count"]),
            "ios" => array($row["apple_count"])
        );
    }else {
        for($i = 0; $i < count($appData); $i++) {
            if($appData[$i]["name"] == $row["name"]){
                $appData[$i]["date"][] = $row["date"];
                $appData[$i]["android"][] = $row["android_count"];
                $appData[$i]["ios"][] = $row["apple_count"];
            }else {
                $appData[] = array(
                    "name" => $row["name"],
                    "date" => array($row["date"]),
                    "android" => array($row["android_count"]),
                    "ios" => array($row["apple_count"])
                );
            }
        }
    }
}
echo json_encode($appData);

When I try to run the code it will give an "Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 71 bytes)" error. I've tried to increase the maximum allowed memory, just to see what would happen, but I got the same result.

Is there any way to avoid having to do so many loops? Or should I approach this from a completely different way, and if so which one?

The end result should look something like this

[{"name":"Some name", "date":["2016-05-09", "2016-05-10", "2016-05-11"], "android":["3", "1", "8"], "ios":["4", "7", "5"]},...]

All help would be appreciated!

1

4 Answers 4

1

The memory problem is in the "for" loop. It can be adding a bunch of items to $appData per loop, instead of "just one if there is no matching name." For example, if $appData already has 100 items in it, and $row['name'] matches the last item in $appData, then 99 items will be added to $appData before the last item in $appData is updated. I bet the current code is generating an $appData with a lot more than 785 items.

To fix the memory issue, change the "for" loop to something like this:

    $matchFound = false;
    for($i = 0; $i < count($appData); $i++) {
        if($appData[$i]["name"] == $row["name"]){
            $appData[$i]["date"][] = $row["date"];
            $appData[$i]["android"][] = $row["android_count"];
            $appData[$i]["ios"][] = $row["apple_count"];
            $matchFound = true;
            break;
        }
    }
    if (!$matchFound) {
        $appData[] = array(
            "name" => $row["name"],
            "date" => array($row["date"]),
            "android" => array($row["android_count"]),
            "ios" => array($row["apple_count"])
        );
    }

On an efficiency note, using an associative array as suggested by maximkou will be a big speedup.

Sign up to request clarification or add additional context in comments.

Comments

1

Your problem is not the number of loops, but the size of your $appData array and the memory_limit value of your php configuration.

If you cannot shrink the size of the data you pass on, you must increase the memory_limit value. But be careful while increasing this value, since it's the value for each running php script your server will execute. I would recommand to paginate or send to the output buffer on each loop.

If you need exemple of code, just ask for it.

Pagination means your javascript page will call X times the PHP script to retrieve N rows each times, until the PHP scripts does not allow it anymore. Therefore you have to return an array such as :

return array(
    'nextPage' => 2, // More data available on this page
    'data' => $json
);

// Or

return array(
    'nextPage' => null, // No more data available
    'data' => $json
);

Or send to the output buffer on each loop and release memory :

$first = true;
echo '[';

while($row = $res->fetch_assoc()) {

    if(!$first) {
        echo ',';
    } else {
        $first = false;
    }

    // some logic
    $row_data = array(...);

    echo json_encode($row_data);
}

echo ']';

This way you do not stack all the data in php's variables.

1 Comment

If you have an example laying around, that would be great!
0

Index your array by $row['name']. This strong simplify your code. Arrays in php allocate many memory, so encode nested data by row. Or try to use SplFixedArray if you know result array size.

Try this:

while($row = $res->fetch_assoc()) {
    $appData[ $row["name"] ] = json_encode(array(
        "name" => $row["name"],
        "date" => array($row["date"]),
        "android" => array($row["android_count"]),
        "ios" => array($row["apple_count"])
    ));
}
echo "[".implode(',', $appData)."]";

Comments

0

This should create the exact same results (tho haven't tested), and uses a map array and array_key_exists() to avoid extra loops. This does it in a single loop.

$nameMap = array(); // hold name and keys
while($row = $res->fetch_assoc()){
    $key = array_key_exists($row['name'], $nameMap) ? $nameMap[$row['name']] : count($appData);
    $nameMap[$row['name']] = $key;
    if(empty($appData[$key])) 
        $appData[$key] = array("name"=>$row['name'], "date"=>array(), "android"=>array(), "ios"=>array());
    $appData[$key]['date'][] = $row['date'];
    $appData[$key]['android'][] = $row['android'];
    $appData[$key]['ios'][] = $row['ios'];
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.