Call methods of objects in array using array_map?
Asked Answered
H

1

39

Suppose I have simple class like:

class MyClass {
  private $_prop;
  public function getProp() {return $this->_prop;}
  [....]
}

Now what I want to do somwhere not in scope of MyClass is to get array of $_prop from array of objects of MyClass($objs). This of course can be done with code like this:

$props = array();
foreach ($objs as $obj) {
    $props[] = $obj->getProp();
}

However this takes quote some lines, esp when formated in this way (and I have to use such formatting). So question is: if it is possible to do this using array_map? One way would be to use create function, but I don't really like that in php(lambdas in php are at least awkward and if i understand correctly its performance is like that of evaled code, but performance is beside the point here). I have tired searching quite a bit and failed to find any definetive answer. But I kinda have a feeling that it's not possible. I tried things like array_map(array('MyClass', 'getProp'), $objs), but that does not work since method is not static.

Edit: I'm using php 5.3.

Humpy answered 27/7, 2011 at 6:53 Comment(9)
What's wrong with the foreach loop? It's only four lines of code. Which PHP version are you using?Lieselotteliestal
It's just that array_map has potential to be way cleaner, and I like functional techniques. But of course php is not a functional language so... Still it would be cool if this could be done. Since in reality it seems like kinda trivial functionality for language to have, kinda.Humpy
What exactly could be cleaner? You pass the content of the array to the callback one by one. That's pretty much the same than in Ruby, Python and JavaScript.Stephanystephen
Pseudo code array_map(array('MyClass', 'getProp'), $objs) where non static method will be called would be cleaner. One compact line. No need to create empty array. No need to manually loop and push objects onto array. Just give one array and take another. Of course this idea does not play well with oop and esp with dynamic lose typing that php uses... But still it could be done :)Humpy
But that's not what array_map does. It does callback($element) for each $element in array. What you describe would be $element->callback() instead.Stephanystephen
In implementation sense yes. But in logical sense I give it array and function(which in oop language I could expect to be able to be objects non static method) and get array that results of applying that function to all elements. After toying a bit with real functional languages like Haskell, one wishes to do such things very simply:) though Haskell is totally not oop:) And object methods are not used there. But man it has functional magic.Humpy
But that is exactly what you get: get array that results of applying that function to all elements. You get f(e) for all e in a. You can pass an non-static object method as a callback when doing array($obj, 'foo') but that still would evaluate to $obj->foo($el) because array_map passes it's elements to that callback. It doesn't invoke a method on the elements. I'm not familiar with Haskell, but a quick glance at the map function doesnt seem to suggest that it's different there.Stephanystephen
Haskell does not have concept of methods at all. Since there is no oop(though I bet you can emulate that somewhat, though I'd never do that:) function seems just so much better for me than oop). But you can have structure that has function as values and you could call them in some way. I'm just not sure now in what way, that is if one would need lambdas (witch are cleaner and more elegant there) or maybe you would need to use some other construct.Humpy
array_map(array('MyClass', 'getProp'), $objs) works (with PHP 5.3.3)Hades
L
37

In PHP 5.3 you can do:

$props = array_map(function($obj){ return $obj->getProp(); }, $objs);

(see anonymous functions)

Of course this will still be slower than using a for loop as you have one function invocation per element but I think this comes closest to what you want.

Alternatively, which also works in prior to PHP 5.3 and might fit better to your style guidelines:

function map($obj) {
    return $obj->getProp();
}

$props = array_map('map', $objs);

Or (again back to PHP 5.3) you could create a wrapper function like this (but this will be the slowest possibility I think):

function callMethod($method) {
    return function($obj) use ($method) {
        return $obj->{$method}();
    };
}

$props = array_map(callMethod('getProp'), $objs);
Lieselotteliestal answered 27/7, 2011 at 7:5 Comment(12)
You could even type hint the closure argument to trigger a fatal error if the object was not an instance of MyClass, eg function(MyClass $obj){...Wisteria
The closure would only be created once when passed to array_map(). At a guess, I'd say this would be almost identical to the loop in terms of performanceWisteria
I said I don't want lambda/anonymous functions. Besides now that I think I most likely could not write this according to coding style guidelines.Humpy
@morphles: Well, in your comment you say you like functional approaches but now you say you don't line anonymous functions. But they go hand and hand. As I said in my comment I would just go with the for loop then.Lieselotteliestal
@Phil: Yes, it is created once but called for each element. It is like having an additional function call in the for loop.Lieselotteliestal
@Felix Ah, misunderstood your use of "invoke"Wisteria
Yeah thats what I'm doing, but I have/had a little hope maybe it was possible without that. And as I said functional stuff in the php is the suck... Another thing to consider here is that there already is function for taking value of property from object, why the hell one should declare another one.Humpy
@morphles: PHP provides us many small functions which can be assembled to new more powerful ones. That's just how it is. It's not Python.Lieselotteliestal
Well third options is really interesting and after some time I'll accept your answer for it. Though my superiors will not like this, and I totally agree about performance. But in the end this way you can kinda add feature that I was missing/requested. You only need to declare one function for all classes and methods of theirs. Though it would be nice to see some link to reference syntax used there, since it is unfamiliar for me.Humpy
@morphles: It uses an anonymous function again (link already provided in the answer) and it is creating a closure through use (there is no documentation for that).Lieselotteliestal
Can this answer be updated some, as I used this in php7.0 - is it not available in some versions, or just below v5.3? If the latter, can it be updated to 'from v5.3...'?eStephanus
for reusablity you should skip annonymus functions and create a mapper class that handles that, so you can reuse the code if needed. encolsures usually tend to become a mess in bigger applications with duplicating code like crazyBerck

© 2022 - 2024 — McMap. All rights reserved.