tags:

views:

74

answers:

4

One of the patterns that I frequently run across when developing is trying to collect a column/attribute value from a collection of objects into an array. For example:

$ids = array();
foreach ($documents as $document) {
    $ids[] =  $document->name;
}

Am I the only one who runs into this? And does PHP have a way to solve this in fewer lines? I've looked but found nothing.

Since I use an MVC framework I have access to a BaseUtil class which contains common functions that don't really fit in any specific classes. One solution proposed by a co-worker is:

class BaseUtil
{
    public static function collect($collection, $property) {
        $values = array();
        foreach ($collection as $item) {
            $values[] = $item->{$property};
        }
        return $values;
    }
}

Then I can just do:

$ids = BaseUtil::collect($documents, 'name');

Not too shabby. Anyone else have any other ideas? And am I crazy or does this seem like a problem that PHP should have solved a long time ago?

A: 

another approach is to use "rich" Array objects, like those found in other languages

for example

 class Ary extends ArrayObject
 {
     function pluck($key) {
        $a = array();
        foreach($this as $sub) $a[] = $sub[$key];
        return new self($a);
     }

     function join($delim = ',') {
         return implode($delim, (array) $this);
     }

     static function init($ary) {
        return new self($ary);
     }
 }

 echo
   Ary::init(array(
      array('foo', 'bar'), array('baz', 'quux')
   ))->pluck(1)->join();
stereofrog
A: 

One of PHP's weaknesses as a language is that it's not terribly expressive.

Where in languages like Ruby or Perl you could probably get this data with a single line of code, you generally need small algorithms like the one you posted to get the desired results.

I'd stick with what you have, but here's another approach just for the heck of it.

class BaseUtil
{
    public static function collect($collection, $property)
    {
      array_walk( $collection, array( __CLASS__, 'reduceObject' ), $property );
      return $collection;
    }

    public static function reduceObject( &$object, $index, $property )
    {
      $object = $object->{$property};
    }
}
Peter Bailey
A: 

Thanks for the input guys. I guess I'm just going to use my coworker's solution:

class BaseUtil
{
    public static function collect($collection, $property) {
        $values = array();
        foreach ($collection as $item) {
            $values[] = $item->{$property};
        }
        return $values;
    }
}
Just4Ever
+2  A: 

You can use array_map() function for this purpose:

function getName($obj) {
    return $obj->name;
}

$documentsName = array_map("getName", $documents);

You might also consider the create_function() function for lambda functions if you don't want to create a getName() function in the global namespace.

In PHP 5.3 you might even do:

$documentsName = array_map(function ($obj) { return $obj->name; }, $documents);
Patrick Allaert
That won't work for my purposes because I want to generalize this for any property/attribute; not just for name. When trying to do that array_map() becomes much more messy and I couldn't get it to work.
Just4Ever