views:

154

answers:

6

PHP functions such as 'array_map' take a callback, which can be a simple function or a class or object method:
$array2 = array_map('myFunc', $array);
or
$array2 = array_map(array($object, 'myMethod'), $array);

But is there a syntax to pass a method which is bound within the iteration to the current object (like 'invoke' in Prototype.js)? So that I could say

$array2 = array_map('myMethod', $array);

with the effect of

foreach($array as $obj) $array2[] = $obj->myMethod();

Obviously I can use this form, or I can write a wrapper function to make the call, and even do that inline. But since 'myMethod' is already a method it seems to be going round the houses to have to do one of these.

+4  A: 

Not currently. When php 5.3 comes out, you could use the following syntax:

$array2 = array_map(function($obj) { return $obj->myMethod(); }, $array);
troelskn
Which is longer and less obvious than my foreach method. Thanks.
Colin Fine
It's ugly alright, but at least it doesn't use temp variables. If you come from a function-oriented language, such as Ruby or lisp, it appears more intuitive.
troelskn
+1  A: 

Basically, no. There is no special syntax to make this any easier.

I can think of a fancier way of doing this in PHP 5.3, seeing as there's always more than one way to do things in PHP, but I'd say it wouldn't necessarily be better than your foreach example:

$x = array_reduce(
    $array_of_objects, 
    function($val, $obj) { $val = array_merge($val, $obj->myMethod()); return $val; },
    array() 
);

Just go with your foreach :)

MathieuK
The third parameter of array_reduce() must be an integer, so this solution wouldn't work. $ReasonsToHatePhp++;
soulmerge
Actually, that appears to be a documentation error. The prototype in ext/standard/array.c:4015 defines it as 'mixed'.
MathieuK
Try running the code. You will notice, that you will receive 0 in the first recursion - and not an empty array.
soulmerge
+2  A: 
function obj_array_map($method, $arr_of_objects) {
    $out = array();
    $args = array_slice(func_get_args(), 2);
    foreach ($arr_of_objects as $key => $obj) {
        $out[$key] = call_user_func_array(Array($obj, $method), $args);
    }
    return $out;
}

// this code
$a = Array($obj1, $obj2);
obj_array_map('method', $a, 1, 2, 3);

// results in the calls:
$obj1->method(1, 2, 3);
$obj2->method(1, 2, 3);
jmucchiello
A: 
<?php

// $obj->$method(); works, where $method is a string containing the name of the
// real method
function array_map_obj($method, $array) {
    $out = array();

    foreach ($array as $key => $obj)
        $out[$key] = $obj->$method();

    return $out;    
}

// seems to work ...

class Foo {
    private $y = 0;
    public function __construct($x) {
        $this->y = $x;
    }
    public function bar() {
        return $this->y*2;
    }
}

$objs = array();
for ($i=0; $i<20; $i++)
    $objs[] = new Foo($i);

$res = array_map_obj('bar', $objs);

var_dump($res);

?>

voila!

Schnalle
A: 

This is a bit of a silly answer, but you could subclass ArrayObject and use that instead of a normal array:

<?php

class ArrayTest extends ArrayObject {
    public function invokeMethod() {
        $result = array();
        $args = func_get_args();
        $method = array_shift($args);
        foreach ($this as $obj) {
            $result[] = call_user_func_array(array($obj, $method), $args);
        }
        return $result;
    }
}

//example class to use
class a { 
    private $a;
    public function __construct($a) { 
        $this->a = $a; 
    }

    public function multiply($n) {
        return $this->a * $n;
    }
 }

//use ArrayTest instance instead of array
$array = new ArrayTest();
$array[] = new a(1);
$array[] = new a(2);
$array[] = new a(3);

print_r($array->invokeMethod('multiply', 2));

Outputs this:

Array
(
    [0] => 2
    [1] => 4
    [2] => 6
)
Tom Haigh
A: 

I would use create_function() to ... well ... create a temporary function for array_map while waiting for PHP 5.3

$func = create_function('$o', '$o->myMethod();');
array_map($func, $objects);
soulmerge
I can't really see any point in using create_function and not use it inline. If you are going to assign it to the variable $func, you might as well create a named function.
Colin Fine
Use it inline, then. I don't like my source code growing too long (horizontally). Besides, storing it in a variable makes it clear to anyone looking at the source code that this function is used nowhere else.
soulmerge