提取数组部分的快速方法?

Fast way to extract portions of array?

提问人:Pekka 提问时间:11/18/2009 更新时间:11/24/2009 访问量:194

问:

我在PHP中有一个很大的数组。

它包含使用下划线拆分为一种类别的字符串:

category1_property
category1_category2_category3
category2_category3_category4_category5

我有一个名为

array get_values($prefix) 

返回以给定前缀开头的数组的所有值,例如

get_values("category2_category3_");

这个函数 foreach()es 每次都会遍历整个数组,收集所有以前缀开头的字符串,即一个简单的

foreach ($my_array as $line)
 if (substr($line, 0, strlen($prefix)) == $prefix)) 
  array_push ($result, $line);

在性能方面,我感觉很糟糕,尤其是看到每个请求执行数十次此操作。

有没有人知道一种方法可以加快速度,而不必诉诸完全不同的数据存储方式?

使用数据库可能既快速又聪明,但我想避免这种情况。数据来自文件,我无法将其移植到数据库。

将构造预先排序或拆分为多维数组或对象不是一种选择,因为我有时需要查询类别名称的各个部分(例如“category1_ca*”)

提前感谢您的任何意见。

PHP 数组 排序

评论

0赞 intgr 11/22/2009
现在有多快,你想加快多少?

答:

1赞 intgr 11/18/2009 #1

为了节省时间进行访问,我认为最简单的解决方案是对数组进行排序,并使用二进制搜索算法的修改变体来查找与您的查询匹配的下限和上限数组边界。这之所以有效,是因为具有相似前缀的字符串始终按顺序排序。

一旦你有了这个范围,获取匹配的元素就是一个简单的for循环。

显然,这不是一项微不足道的任务,所以不要浪费任何时间,除非这真的是一个性能问题。过早优化,你懂钻...

1赞 Kristoffer Bohmann 11/18/2009 #2

我不清楚get_values功能应该匹配什么 - 无论如何,这可能是您正在寻找的性能友好型解决方案?

function get_values($prefix) {
    $included_array_from_file = array ( "category1_property", "category1_category2_category3", "category2_category3_category4_category5");

    foreach($included_array_from_file as $val) {
        if(strpos($val,$prefix)===0) {
            $out[] = $val;
        }
    }
    return $out;
}

print_r( get_values("category2_category3_") );

输出:
Array ( [0] => category2_category3_category4_category5 )

更新:

你需要计算字符串中“category2_category3_”出现多少次,对吧?在这种情况下,我建议您为完整字符串创建一个多维数组,并计算每次出现次数,如以下示例所示: (请注意,该示例仅说明了如何完成 - 该示例目前失败,因为我不确定如何动态构建多维数组,您可能需要在向数组添加项目时调用另一个“创建数组”函数。

失败(“无法将标量值用作数组”) - 不确定如何操作。

$data = array("category1_property", "category1_category2_category3", "category2_category3_category4_category5");
$counter = array();
foreach($data as $val) {
    foreach(explode(":",$val) as $val2) {
        // Now, create a multi-dimensional array with the category items as keys and increment the value by one for each item in the string, as in this example:
        // "category2_category3_category4_category5" ... turns into:
        // $counter[category2] += 1;
        // $counter[category2][category3] += 1;
        // $counter[category2][category3][category4] += 1;
        // $counter[category2][category3][category4][category5] += 1;
    }
}

预期用途:

echo $counter[category2][category3];

评论

0赞 Pekka 11/22/2009
这就是我现在正在做的事情。我担心调用get_values()一百次(有一百个循环)会很耗费性能。我可能不会去做某种预排序。
1赞 Matteo Riva 11/18/2009 #3

我想你正在寻找preg_grep

1赞 dnagirl 11/18/2009 #4

你真的限制了选择!即便如此,我认为预先拆分数据可能是要走的路。考虑:

前缀 , , 变为'cat1_cat2_cat3_dog'='fido''cat1_cat2_cat3_fish'='goldie''cat1_cat2_cat3_frog'='kermit

$arr[cat1][cat2][cat3][dog]=fido
$arr[cat1][cat2][cat3][fish]=goldie
$arr[cat1][cat2][cat3][frog]=kermit

如果您想要所有带有前缀的内容:cat1_cat2

$arr['cat1']['cat2']=array('cat3'=>array('dog'=>'fido','fish'=>'goldie'));

如果您想要所有带有前缀的内容,则只需搜索以下术语:cat1_cat2_cat3_f*$arr['cat1']['cat2']['cat3']

$matches=preg_grep("/^f/",array_keys($arr['cat1']['cat2']['cat3']));
foreach($matches as $k){
   $results[]=$arr['cat1']['cat2]['cat3'][$k];
}

评论

0赞 Pekka 11/22/2009
我可能不会绕过预先拆分它们。感谢您的输入。
0赞 GZipp 11/18/2009 #5

或者你可以在 array_filter() 中使用匿名函数:

function get_values($arr, $str)
{
    $func = create_function('$item', 'return (strpos($item, "' . $str . '") === 0);');
    return array_filter($arr, $func);
}

$prefix = 'category1';
$result = get_values($my_array, $prefix);

评论

0赞 Pekka 11/22/2009
当多次调用时,这与我目前使用的函数具有相同的缺点:它总是必须遍历整个数组。
0赞 GZipp 11/26/2009
当然,它会遍历整个数组。你还将如何检查数组中的每个项目?(因为您已经排除了任何其他选项,例如“完全不同的存储方式”(例如缓存结果、使用数据库等))这种方法很快,但我承认它不是神奇的瞬间。