How to make array loop faster in PHP - php

Is there a way to make this faster?
while ($item = current($data))
{
echo '<ATTR>',$item, '</ATTR>', "\n";
next($data);
}
I do not like that I need to create new variables like $item.

<?php
$transport = array('foot', 'bike', 'car', 'plane');
foreach ($transport as $value) {
echo $value;
}
?>

If you don't want to create temporary variables, do it like this:
while (current($data))
{
echo '<ATTR>',current($data), '</ATTR>', "\n";
next($data);
}
However, I don't know if this will really make it any faster. They only way to tell would be with a profiler, but it is such a micro-optimization I doubt you will notice the difference.
The best way to speed up the loop would be to use a faster computer.

If all you're doing is the code above you could use an implode statement.
if (count($data) > 0) {
echo "<ATTR>".implode("</ATTR>\n<ATTR>", $data)."</ATTR>";
}

$nl = "\n";
while ($item = current($data))
{
echo '<ATTR>',$item, '</ATTR>',$nl;
next($data);
}
Store the newline character into a variable rather then having PHP parse the double quotation marks in every iteration.

I do a little bench to comprobe it.
<?php
$a = array();
for ($i = 0; $i < 100000; $i++) {
$a[] = $i;
}
$start = microtime(true);
foreach ($a as $k => $v) {
$a[$k] = $a[$k] + 1;
}
echo "foreach : ", microtime(true) - $start, " Seconds\n";
$start = microtime(true);
foreach ($a as $k => &$v) {
$v = $v + 1;
}
echo "foreach with cursor : ", microtime(true) - $start, " Seconds\n";
$start = microtime(true);
for ($i = 0; $i < count($a); ++$i) {
$a[$i] = $a[$i] + 1;
}
echo "for : ", microtime(true) - $start, " Seconds\n";
$start = microtime(true);
for ($i=0,$l=count($a);$i<$l;++$i) {
$a[$i] = $a[$i] + 1;
}
echo "for with cached count : ", microtime(true) - $start, " Seconds\n";
With results
foreach : 0.0039410591125488 Seconds
foreach with cursor : 0.00357985496521 Seconds
for : 0.0022602081298828 Seconds
for with cached count : 0.0020480155944824 Seconds
Hope this helps

You could do a foreach, but then you would be creating 2 new variables. Unless you just don't like the idea of assigning variables inside the while() clause.
foreach($data as $key => $value)
{
echo $key . " => ".$value;
}
Either way, you are going to need to create an actual variable.

What about this one :
function my_func($str) {
echo "<attr>{$str}</attr>\n";
}
array_map('my_func', $data);
(Should work, but I'm curious about it's speed compared with a foreach loop)
Or, if you are using PHP >= 5.3 (probably not your case, btw), you can use this one, based on a lambda function :
array_map(function ($item) {
echo "<attr>{$item}</attr>\n";
}, $data);
Almost the same, but without having to declare a function used only once in the program.

Related

How to simplify a nested loops?

I have 2 ranges I am currently looping through.
$arr1 = range(1500, 1505);
$start = 1;
$end = 10;
foreach ($arr1 as $block) {
for ($i = $start; $i <= $end; $i++) {
echo $block . $i; // output -> 15001,15002,15003 ... 15011, 15012 ...
}
}
Is there an easier / more efficient way to do this?
I believe you can do it like this:
for($start = 15001; $start < 15061 ; $start = $start + 10 ) {
$arr = range($start, $start + 8);
$arr[] = $start * 10;
echo implode(" ", $arr) . PHP_EOL;
}
However, I still not get the pattern you trying the create...
Another slightly more efficient approach would be to create the entire range at once. Then use modulo to determine the values divisible by 10, and use mathematics to output the desired alternative value.
Example: https://3v4l.org/L10Jq
foreach (range(15001, 15060) as $v) {
if (0 === $v % 10) {
echo ($v - 9) * 10 . \PHP_EOL;
} else {
echo $v . ' ';
}
}
Result:
15001 15002 15003 15004 15005 15006 15007 15008 15009 150010
15011 15012 15013 15014 15015 15016 15017 15018 15019 150110
15021 15022 15023 15024 15025 15026 15027 15028 15029 150210
15031 15032 15033 15034 15035 15036 15037 15038 15039 150310
15041 15042 15043 15044 15045 15046 15047 15048 15049 150410
15051 15052 15053 15054 15055 15056 15057 15058 15059 150510
To create a single array, pass the value $v by-reference, remove the echo calls and re-assign the value in the conditional.
$ar = range(15001, 15060);
foreach ($ar as &$v) {
if (0 === $v % 10) {
$v = ($v - 9) * 10;
}
}

Why is passing by reference slower in this code?

I've encountered something that seems like a strange performance problem. Running this code:
<?php
function test_ref(&$test)
{
for ($i = 0; $i < 100000; $i++)
{
$foo = "s" . rand(1, 1000);
if (!array_key_exists($foo, $test))
{
$test[$foo] = array();
}
$test[$foo][] = rand(1, 10);
}
}
function test()
{
$test = array();
for ($i = 0; $i < 100000; $i++)
{
$foo = "s" . rand(1, 1000);
if (!array_key_exists($foo, $test))
{
$test[$foo] = array();
}
$test[$foo][] = rand(1, 10);
}
return $test;
}
$scriptstart = microtime(true);
$test = array();
test_ref($test);
$sum = 0;
foreach ($test as $key => $val)
{
foreach ($val as $val2)
{
$sum += $val2;
}
}
echo "sum " . $sum . "<br>";
$scriptelapsed = microtime(true) - $scriptstart;
echo "time taken " . $scriptelapsed . "<br>";
$scriptstart = microtime(true);
$test = test();
$sum = 0;
foreach ($test as $key => $val)
{
foreach ($val as $val2)
{
$sum += $val2;
}
}
echo "sum " . $sum . "<br>";
$scriptelapsed = microtime(true) - $scriptstart;
echo "time taken " . $scriptelapsed . "<br>";
?>
I get these results:
sum 548521
time taken 12.37544798851
sum 551236
time taken 0.29530310630798
What's going on here? It seems to be connected to the fact that I insert sub-arrays into the array, though I don't see why passing by reference should be that much slower.
(this is on PHP Version 5.3.3-7+squeeze14 with Suhosin Patch 0.9.9.1)
(edit: fixed using of unset variables, still the same result)
You're accessing values from another scope - that's always slower than only using ones that are defined within the function itself. Here's a nice blog post explaining it, even though that's not its primary topic: PHP internals: When does foreach copy?
It is just my guess but I think we could explain it like this:
when using no reference a local variable is created (that is directly pointing to a memory) and the loop goes like:
$i = 0; $foo = 499; $test[499] = array(); $test[499][] = 2; "commit" directly to a memory
finaly, the value $test is then returned - reading directly from the memory pointer
when using referenced value, the variable passed is like a pointer to a pointer (that is finally pointing to a memory)
in this case the loop looks like:
$i = 0; $foo = 354; $test[354] = array(); $test[354][] = 7; "commit" to a memory via pointer to a memory pointer
I guess therefore there is at least one more step neccessary when working with referenced variables...

Fixtures PHP algorithm

Struggling with this tournament fixtures algorithm.
The code is working perfectly but I need help inserting the data to mysql
I cant seem to access the $varables..
any tweaking by a php pro greatly appreciated ...
$teamnames = "Arsenal|Tottenham|Leeds|Man United|Liverpool";
# XXX check for int
print show_fixtures(isset($_GET['teams']) ? nums(intval($_GET['teams'])) : explode("|", trim($teamnames)));
function nums($n) {
$ns = array();
for ($i = 1; $i <= $n; $i++) {
$ns[] = $i;
}
return $ns;
}
function show_fixtures($names) {
$teams = sizeof($names);
print "<p>Fixtures for $teams teams.</p>";
// If odd number of teams add a "ghost".
$ghost = false;
if ($teams % 2 == 1) {
$teams++;
$ghost = true;
}
// Generate the fixtures using the cyclic algorithm.
$totalRounds = $teams - 1;
$matchesPerRound = $teams / 2;
$rounds = array();
for ($i = 0; $i < $totalRounds; $i++) {
$rounds[$i] = array();
}
for ($round = 0; $round < $totalRounds; $round++) {
for ($match = 0; $match < $matchesPerRound; $match++) {
$home = ($round + $match) % ($teams - 1);
$away = ($teams - 1 - $match + $round) % ($teams - 1);
// Last team stays in the same place while the others
// rotate around it.
if ($match == 0) {
$away = $teams - 1;
}
$rounds[$round][$match] = team_name($home + 1, $names)
. " v " . team_name($away + 1, $names);
}
}
// Interleave so that home and away games are fairly evenly dispersed.
$interleaved = array();
for ($i = 0; $i < $totalRounds; $i++) {
$interleaved[$i] = array();
}
$evn = 0;
$odd = ($teams / 2);
for ($i = 0; $i < sizeof($rounds); $i++) {
if ($i % 2 == 0) {
$interleaved[$i] = $rounds[$evn++];
} else {
$interleaved[$i] = $rounds[$odd++];
}
}
$rounds = $interleaved;
// Last team can't be away for every game so flip them
// to home on odd rounds.
for ($round = 0; $round < sizeof($rounds); $round++) {
if ($round % 2 == 1) {
$rounds[$round][0] = flip($rounds[$round][0]);
}
}
// Display the fixtures
for ($i = 0; $i < sizeof($rounds); $i++) {
print "<hr><p>Round " . ($i + 1) . "</p>\n";
foreach ($rounds[$i] as $r) {
print $r . "<br />";
}
print "<br />";
}
print "<hr>Second half is mirror of first half";
$round_counter = sizeof($rounds) + 1;
for ($i = sizeof($rounds) - 1; $i >= 0; $i--) {
print "<hr><p>Round " . $round_counter . "</p>\n";
$round_counter += 1;
foreach ($rounds[$i] as $r) {
print flip($r) . "<br />";
}
print "<br />";
}
print "<br />";
if ($ghost) {
print "Matches against team " . $teams . " are byes.";
}
}
function flip($match) {
$components = split(' v ', $match);
return "$components[1]" . " v " . "$components[0]";
}
function team_name($num, $names) {
$i = $num - 1;
if (sizeof($names) > $i && strlen(trim($names[$i])) > 0) {
return trim($names[$i]);
} else {
return "BYE";
}
}
I'm not entirely sure what you're hung up on (you should really be more specific in your questions, as specified by the FAQ), but I suspect it is a matter of scope.
When you set a variable within a function, that variable is only accessible within that function. For example:
function do_something() {
$a = 'something!';
}
do_something();
echo $a;
This should result in PHP notice telling you that PHP doesn't know what $a is in the scope that it is trying to echo. Now, if I modify this script...
$a = '';
function do_something() {
global $a; // Lets PHP know we want to use $a from the global scope
$a = 'something!';
}
do_something();
echo $a;
This will work and output "something!", because $a is being "defined" in the scope outside of the function.
You can read more about variable scope in the PHP documentation: http://php.net/manual/en/language.variables.scope.php
Now, something else you need to pay attention to is outputting user data. In your script, you take data straight from $_GET and print it out to the page. Why is this bad? Someone could inject some nice JavaScript into your page (or anything they wanted) and steal users' sessions. You should be using htmlspecialchars() any time you need to output a variable. Even if it is just a team name, you never know when some team will stick a ; or < or > or some other reserved character in there.
Finally, I strongly recommend not mixing your logic with your computation. Let your program figure everything out, and then loop through the data for your output. You should be able to save the entire data for this type of problem in a nice associative array, or some crafty object you come up with.

PHP Return Loop Result

I am new to the world of coding as well as PHP and am wondering how I can use return when looping. For example I would like to return/display 1-10 however not use echo.
$start = 1;
$end = 11;
for($start; $start < $end; $start=$start+1) {
echo $start; //how can I use return?
}
Well, return will exit the function, so if you put return in a loop, the loop will only do one iteration (until the return statement).
You can collect all the values in an array and return the array:
function myFunction() {
$start = 1;
$end = 11;
$values = array();
for($start; $start < $end; $start=$start+1) {
$values[] = $start;
}
return $values;
}
That said, a function generating consecutive numbers already exists: range().
return is for sending the results of a function call back to the function or script that called it. It's the opposite of passing parameters to a function.
What you're doing is looping over a variable in the same scope, so return is not needed. Printing is done via echo or print. However, you may choose to build a value in that loop and print that once the loop is completed.
Additionally, if you're in a loop and you want to stop that loop immediately, use break; and if you want to skip the iteration you're on and go to the next one, use continue.
Here's some additional reading.
More clarification on continue. Say, for whatever reason, we don't want to do anything when $i is 6:
$start = 1;
$end = 11;
for ($i = $start; $i < $end; $i++)
// changed this to iterate over $i for readability/clarity
{
if ($start == 6)
{
// essentially, continue just skips this iteration of
// the loop, goes back to the top, iterates $i based on
// that third parameter in the for() declaration, and
// continues on.
continue;
}
echo $start; //how can I use return?
}
// output: 1234578910
just use
function foobar()
{
$output = '';
for ( $i = 1 ; $i <= 10 ; $i++ )
{
$output .= $i;
}
return $output
}
echo foobar();
You can achieve it by defining a variable you want to return inside and outside the loop with .=.
Here is a working example:
function my_function() {
// I am the variable to be return-ed
$k = "My World<br/>";
for ($z=1; $z<=3; $z++) {
$v = 'Here'.$z;
// I am the same variable but I am going inside the loop
$k .= $v . "<br/>";
}
//I am not always important to be here!
$k .= "Is Awesome";
// This is the show time..
return $k;
}
my_function();
Output:
My World
Here1
Here2
Here3
Is Awesome
Return will end the function that you are currently in. The scenario you have given us is not a good scenario to use the return function.
The example below will do the same as your code, but uses a function as well as your for loop. Hope this answeres your question.
$start = 11;
$end = 20;
for($start; $start <= $end; $start++){
echo calculateValue($start);
}
function calculateValue($value){
return $value - 10;
}
function loop_kec($ar_kec) {
$total_data = count($ar_kec) - 1;
//$return_kec = array();
for ($e = 0; $e < $total_data; $e++) {
$return_kec .= 'kecamatan = ' . "'$ar_kec[$e]'" . ' or ';
}
$return_kec .= 'kecamatan = ' . "'$ar_kec[$total_data]'";
return $return_kec;
}

How to produce a unique string from a php array

I need a unique string from an array so that I can tell when it changes without measuring the inputs of that array. I'm trying to work out if it is computationally efficient to calculate a value rather than add code to look out for changes in the array. The array itself can have a variety of values and for future proofing I don't want to try and measure whether new values have been added to the array, I'd much rather just create some string or hash that will change if the array itself changes.
So for example:
$a = Array(
'var1' => 1,
'var2' => 2,
'var3' => 3,
);
If I was to use md5(http_build_query($a)) perhaps with an added ksort to confirm that the order of the keys haven't changed this might then produce a unique string that I can use to compare against another run of the application to evaluate whether the array has changed.
I'm looking for an alternate, possibly faster or more elegant solutions to this.
Im use md5(serialize($array)) for this. Its better, because works for multi-dimensional arrays.
Thanks for all the ideas guys.
I've tried all of them except a sha-256 which my server doesn't have installed.
Here's the results:
Average (http_build_query): 1.3954045954045E-5
Average (diff): 0.00011533766233766
Average (serialize): 1.7588411588412E-5
Average (md5): 1.6036963036966E-5
Average (implode-haval160,4): 1.5349650349649E-5
That's running the operation 1000 times and averaging the result. After refreshing a couple times I could tell that the http_build_query was the quickest. I guess my next question would be if anyone can think of any pitfalls of using this method?
Thanks
Here's my code:
class a {
static $input;
function test() {
$start = null;
$s = $e = $d = $g = $h = $i = $k = array();
self::$input = array();
for ($x = 0; $x <= 30; $x++) {
self::$input['variable_' . $x] = rand();
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = http_build_query(self::$input);
($c == $c);
$s[] = microtime() - $start;
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = md5(http_build_query(self::$input));
($c == $c);
$e[] = microtime() - $start;
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = array_diff(self::$input, self::$input);
$d[] = microtime() - $start;
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = serialize(self::$input);
($c == $c);
$g[] = microtime() - $start;
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = hash("haval160,4", implode(',',self::$input));
($c == $c);
$h[] = microtime() - $start;
}
echo "<pre>";
//print_r($s);
echo "Average (http_build_query): " . array_sum($s) / count($s) . "<br>";
echo "Average (diff): " . array_sum($d) / count($d) . "<br>";
echo "Average (serialize): " . array_sum($g) / count($g) . "<br>";
echo "Average (md5): " . array_sum($e) / count($e). "<br>";
echo "Average (implode-haval160,4): " . array_sum($h) / count($h);
}
}
a::test();
PHP has an array_diff() function, don't know if it's of any use for you.
Otherwise, you can eventualy use the incremental hashing possibility offered by php : http://www.php.net/manual/en/function.hash-init.php by iterating over each values of the array and adding them in the incremental hash.
You could always just do
$str = implode(",", $a);
$check = hash("sha-256", $str);
Theoretically, that should detect changes in array size, data, or ordering.
Of course, you can use whatever hash you wish.

Categories