开发者

Why does this loop give up halfway through?

开发者 https://www.devze.com 2023-04-01 04:48 出处:网络
$list is a very large array with a series of words, some of which are greater than 5 characters and some of which are smaller. This loop will unset all values w开发者_开发问答here the length of the wo

$list is a very large array with a series of words, some of which are greater than 5 characters and some of which are smaller. This loop will unset all values w开发者_开发问答here the length of the word is longer than 5 chars.

for($i = 0; $i < count($list); $i++)
{
    $list[$i] = trim($list[$i]);
    if(strlen($list[$i]) > 5)
    {
        unset($list[$i]);
    }
}

However, it does that for about half of $list and cuts out dead, there's words which are significantly longer than 5 letters still left in the list, but only starting approximately halfway through. What makes it quit? Should I run that loop multiple times?


You're calling count every time, and you're unsetting things during the loop. Call count only once, before the loop. This is good practice to do anyways, as it greatly reduces the number of function calls you have to make.

$count = count($list);
for($i = 0; $i < $count; $i++)
{
    $list[$i] = trim($list[$i]);
    if(strlen($list[$i]) > 5)
    {
        unset($list[$i]);
    }
}

Or just for fun, use array_map/array_filter:

$list = array_map('trim', $list);
$list = array_filter($list, function($s) { return strlen($s) <= 5; });


It fails because you're incrementing i while shrinking the total length of the array, which means at some point i is longer than the current array length.

A safer way of doing this is to count backwards:

for ( $i = count($list); $i--; )
{
    $list[$i] = trim($list[$i]);
    if(strlen($list[$i]) > 5)
    {
        unset($list[$i]);
    }
}


Count($list) is changing as you unset $list elements so every iteration count returns a smaller value.


The count() is the problem. As a solution, I'm partial to foreach loops.

foreach ($list as $k => $v) {
    $list[$k] = trim($v);

    if (strlen($list[$k]) < 5) {
        unset($list[$k]);
    }
}

I think they offer a bit better clarity than the array_ functions.


You need to loop through from count($list) down to 0; when you remove starting at the beginning, you actually have fewer items by the time you get to the end.

It would also be useful to store count($list) before the loop instead of evaluating it each time.


Works amazingly fast in my netbook's poor hardware with a 10000 list of words from www.lipsum.com

function max_len_filter($var) {
    if(strlen($var) > 5) return false;
    return true;
}
$words = array_filter($words, 'max_len_filter');
var_dump($words);

Hope it works for you


Every time unset() is called the length of the array changes. Just store it in a variable before the loop:

$count = count($list);
for($i = 0; $i < $count; $i++)
{
   // your code
}


As you unset() elements, count() decreases. This means that what you have describe is very likely to happen. You would be better calling count() once before the loop, storing it in a variable, and evaluating that at the top of the loop.

0

精彩评论

暂无评论...
验证码 换一张
取 消