在数组的列上应用array_map

时间:2016-05-01 11:49:22

标签: php arrays multidimensional-array

我试图找到一个解决方案来加速PHP的过程。 我们在特定页面上遇到执行时间问题。 我们有一个大约10,000行的数组,我们需要在该数组的某些列上应用几个回调函数。

最好的方法和最快的执行可以越过一个数组,只在几列上应用回调。

<?php
$records = [
    ['id' => 2135, 'first_name' => 'John', 'price' => 1000, 'unit' => 5, 'discount' => 30],
    ['id' => 3245, 'first_name' => 'Sally', 'price' => 2000, 'unit' => 8, 'discount' => 80],
    ['id' => 5342, 'first_name' => 'Jane', 'price' => 4000, 'unit' => 5, 'discount' => 34],
    ['id' => 5623, 'first_name' => 'Peter', 'price' => 1500, 'unit' => 4, 'discount' => 25]
];
function simpleMultiply($value)
{
    return $value * 2;
}

$applyToColumn = ['price','unit','discount'];

// $expectedRecords = array_map('simpleMultiply', array_column($records, 'id'));


$expectedRecords = [
    ['id' => 2135, 'first_name' => 'John', 'price' => 2000, 'unit' => 10, 'discount' => 60],
    ['id' => 3245, 'first_name' => 'Sally', 'price' => 4000, 'unit' => 16, 'discount' => 160],
    ['id' => 5342, 'first_name' => 'Jane', 'price' => 8000, 'unit' => 10, 'discount' => 68],
    ['id' => 5623, 'first_name' => 'Peter', 'price' => 3000, 'unit' => 8, 'discount' => 50]
];
?>

2 个答案:

答案 0 :(得分:2)

如果数组很大,

array_column是一项昂贵的操作,因为它必须创建一个全新的数组。对不同的列重复调用它会使该费用倍增。我建议你只使用一个简单的foreach循环。

$expectedRecords = array();
foreach ($records as $r) {
    foreach ($applyToColumn as $col) {
        $r[$col] = simpleMultiply($r[$col]);
    }
    $expectedRecords[] = $r;
}

如果您可以修改原始$records而不是创建新的$expectedRecords,则可以在第一个foreach中使用引用。

foreach ($records as &$r) {
    foreach ($applyToColumn as $col) {
        $r[$col] = simpleMultiply($r[$col]);
    }
}

答案 1 :(得分:0)

您可以将结果集包装在iterator(或generator中,它看起来与迭代器非常相似),在将其作为迭代结果返回之前修改当前行,例如

<?php
class Foo extends IteratorIterator {
    public function current() {
        $rv = parent::current();
        if ( $rv ) {
            // ... and more checks here ....
            // this example just assumes the fields/elements exist
            $rv['x'] *= 17;
            return $rv;
        }
    }
}

$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly', array(
    PDO::ATTR_EMULATE_PREPARES=>false,
    PDO::MYSQL_ATTR_DIRECT_QUERY=>false,
    PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION
));
setup($pdo);


// the point is not to have the complete result set in memory
// see: e.g. php.net/mysqlinfo.concepts.buffering (or whatever database you use ;-) )
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$result = new Foo( $pdo->query('SELECT x,y FROM sofoo') );

foreach( $result as $row ) {
    echo join(', ', $row), PHP_EOL;
}


function setup($pdo) {
    $pdo->exec('
        CREATE TEMPORARY TABLE sofoo(
            id int auto_increment, # I just throw this in casually....
            x int,
            y int,
            primary key(id)
        )
    ');
    $stmt = $pdo->prepare('INSERT INTO sofoo (x,y) VALUES (?,?)');
    foreach( range(1,5) as $x ) {
        $stmt->execute( array($x, $x*13) );
    }
}

或(没有大部分样板,使用生成器 - 并立即调用匿名函数)

$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$result = (function($iter) {
    foreach( $iter as $row ) {
        $row['x'] *= 17;
        yield $row;
    }
})($pdo->query('SELECT x,y FROM sofoo') );


foreach( $result as $row ) {
    echo join(', ', $row), PHP_EOL;
}

你可以建立这个&#34;系统&#34;任意复杂。也许传递字段名称和函数来修改元素到生成器,如

$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$result = my_generator(
    $pdo->query('SELECT x,y FROM sofoo'),
    array('x'=>function($e) { return $e*17; })
);
foreach( $result as $row ) {
    echo join(', ', $row), PHP_EOL;
}

function my_generator($iter, $modifiers) {
    foreach( $iter as $row ) {
        foreach( $modifiers as $field=>$func) {
            $row[$field] = $func($row[$field]);
        }
        yield $row;
    }
}

或添加一些链接机制,以便多个函数可以修改相同的字段。或者将完整的行传递给函数,以便它们也可以更改结构。或......或......