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?
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);
You can do it easily with ouzo goodies
$names = array_map(Functions::extract()->name, $documents);
or with Arrays (from ouzo goodies)
$names = Arrays::map($documents, Functions::extract()->name);
You can even extract nested fields of methods calls or array access etc:
$names = Arrays::map($documents, Functions::extract()->getAuthor()->roles[0]);
Check out: http://ouzo.readthedocs.org/en/latest/utils/functions.html#extract
See also functional programming with ouzo (I cannot post a link).
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();
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};
}
}
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;
}
}
Loading a Magento collection, and than looping again in that collection so you can add your desired values into an array it's ineffective. The appropriate way to do this is using getColumnValues() method. This method will give you an array of values by specifying the column name.
Here is the appropriate way of doing this.
$collection =Mage::getModel('your/object')->getCollection()
->addFieldToSelect('customer_id');
$yourArray = $collection->getColumnValues('customer_id');
This will give you an array with all customer_id values you selected.
Related
I use external xml, this works, but I have another problem. In my __construct I have $elem which is responsible for filtering the xml data. But it not working. Please help. I dont how to bite it.
class Property {
public $xmlClass;
public $array = [];
public $elem = '';
public function __construct($xml,$elem) {
$this->xmlClass=$xml;
foreach($xml->list->film->$elem as $result) {
$array = array_push($result);
}
}
}
$result_scenario = new Property($xml,'scenario');
print_r($result_scenario);
The array_push method does not work like that.
You need to pass the array as the first parameter, than pass the element you want to push on it. In your case, it would be array_push($this->array, $result);. The function returns the new number of elements in the array.
See the documentation here http://php.net/manual/en/function.array-push.php
I have a lot of code in some classes, when I must pre-declare array $keys = [];:
public function convertIdStringToMongoID($array_id = array())
{
$keys = [];
foreach ($array_id as $k => $id) {
$keys[] = new \MongoId($id);
}
return $keys;
}
It looks now good, how to make this more beauty?
According to this docs can try mapping your array, something like this:
public function convertIdStringToMongoID($array_id = array())
{
$func = function($id) {
return new \MongoId($id);
};
return array_map($func, $array_id);
}
Or according to this example, something like:
public function convertIdStringToMongoID($array_id = array())
{
return array_map(array($this, 'to_id'), $array_id);
}
private function to_id($id) {
return new \MongoId($id);
}
Both examples use functional programming approach.
The code you posted is clean and fast. You can write it in a more compact way using array_map() and an anonymous function:
public function convertIdStringToMongoID(array $array_id = array())
{
return array_map(function ($id) { return new \MongoId($id); }, $array_id);
}
Some people might consider it more beautiful, others will say it is slightly more difficult to read and understand it this way.
Both will agree that the execution time of this version is slightly longer than your version. However, the difference is not significant, there are other places to search for optimizations (access to disk and external resources, database queries, Mongo etc.)
Ive got this snippet of code below which works perfectly fine. I have been profiling it and the bit of code gets used alot of times, so I want to try figure out how to write it in a way that will perform better than the current way its written.
Is there a more efficient way to write this?
function objectToArray($d) {
if (is_object($d)) {
// Gets the properties of the given object
// with get_object_vars function
$d = get_object_vars($d);
}
if (is_array($d)) {
// Return array converted to object Using __FUNCTION__ (Magic constant) for recursive call
return array_map(__FUNCTION__, $d);
}
else {
// Return array
return $d;
}
}
You could implement a toArray() method to the class that needs to be converted:
e.g.
class foo
{
protected $property1;
protected $property2;
public function __toArray()
{
return array(
'property1' => $this->property1,
'property2' => $this->property2
);
}
}
Having access to the protected properties and having the whole conversion encapsulated in the class is in my opinion the best way.
Update
One thing to note is that the get_object_vars() function will only return the publically accessible properties - Probably not what you are after.
If the above is too manual of a task the accurate way from outside the class would be to use PHP (SPL) built in ReflectionClass:
$values = array();
$reflectionClass = new \ReflectionClass($object);
foreach($reflectionClass->getProperties() as $property) {
$values[$property->getName()] = $property->getValue($object);
}
var_dump($values);
depends what kind of object it is, many standard php objects have methods built in to convert them
for example MySQLi results can be converted like this
$resultArray = $result->fetch_array(MYSQLI_ASSOC);
if its a custom class object you might consider implementing a method in that class for that purpose as AlexP sugested
Ended up going with:
function objectToArray($d) {
$d = (object) $d;
return $d;
}
function arrayToObject($d) {
$d = (array) $d;
return $d;
}
As AlexP said you can implement a method __toArray(). Alternatively to ReflexionClass (which is complex and expensive), making use of object iteration properties, you can iterate $this as follow
class Foo
{
protected $var1;
protected $var2;
public function __toArray()
{
$result = array();
foreach ($this as $key => $value) {
$result[$key] = $value;
}
return $result;
}
}
This will also iterate object attributes not defined in the class: E.g.
$foo = new Foo;
$foo->var3 = 'asdf';
var_dump($foo->__toArray());)
See example http://3v4l.org/OnVkf
This is the fastest way I have found to convert object to array. Works with Capsule as well.
function objectToArray ($object) {
return json_decode(json_encode($object, JSON_FORCE_OBJECT), true);
}
This thread didn't helped me.
If I use
$class_vars = get_class_vars(get_class($this));
foreach ($class_vars as $name => $value) {
echo "$name : $value\n";
}
I get
attrib1_name : attrib2_name : attrib3_name
There are no values. Also a private attribute is shown, which I don't want.
If I use
echo "<pre>";
print_r(get_object_vars($this));
echo "</pre>";
I get
Array
(
[atrrib1_name] => attrib1_value
[attrib2_name] => attrib2_value
)
Here again I have a private attribute and all sub attributes. But this time I have the values. How can I constrain this to one level?
Isn't there a possibility to show all public attributes with their values of an object?
You are seeing non-public properties because get_class_vars works according to current scope. Since you are using $this your code is inside the class, so the non-public properties are accessible from the current scope. The same goes for get_object_vars which is probably a better choice here.
In any case, a good solution would be to move the code that retrieves the property values out of the class.
If you do not want to create a free function for that (why? seriously, reconsider!), you can use a trick that involves an anonymous function:
$getter = function($obj) { return get_object_vars($obj); };
$class_vars = $getter($this);
See it in action.
Update: Since you are in PHP < 5.3.0, you can use this equivalent code:
$getter = create_function('$obj', 'return get_object_vars($obj);');
$class_vars = $getter($this);
You can do this easily with php Reflection api
Extending Mr.Coder's answer, here is a snippet to fetch the public attributes of the object (name and value) as an array
public function getPublicProperties()
{
$results = [];
$reflectionObject = (new ReflectionObject($this));
$properties = $reflectionObject->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
$results[$property->getName()] = $property->getValue($this);
}
return $results;
}
Use get_object_vars.
see: http://dk.php.net/manual/en/function.get-object-vars.php
I Fully recognize what you are trying to achieve so why not have something external like this to help out... (pasted from PHPFiddle)
<?php
final class utils {
public static function getProperties(& $what) {
return get_object_vars($what);
}
}
class ball {
var $name;
private $x, $y;
function __construct($name,$x,$y) {
}
function publicPropsToArray() {
return utils::getProperties($this);
}
function allPropsToArray() {
return get_object_vars($this);
}
}
$ball1 = new ball('henry',5,6);
//$ball2 = new ball('henry',3,4);
echo "<pre>";
print_r($ball1->publicPropsToArray());
echo "\r\n\r\n";
print_r($ball1->allPropsToArray());
echo "\r\n\r\n";
?>
This way I can both access all properties of the object or for something such as a database access layer or similarly for a function that send "safe" data to a view or another un-privileged model I can send just the public properties, but have the behaviour defined within the object.
Sure this leads to coupling with a utility class, but to be fair not all couplings are bad, some are nesecarry to achieve an end goal, dont get bogged down by these things
I have an array of objects and I want to convert it to an array of the result of a method of each of them. I can do this just fine, but I'm wondering if there is a cleaner / better approach to it maybe? For example, pretend this is what I'm working with and how I'm doing it now:
$objects = array();
$objects[] = new Dog();
$objects[] = new Dog();
$objects[] = new Dog();
$data = array();
foreach ($objects as $obj) {
$data[] = $obj->myMethod();
}
Obviously this isn't super important, but it'd be nice to know about better ways to produce $data from $objects in the future. Any ideas? I was thinking there was some function for this, like array_map() or something but I'm not finding it.
you could indeed use PHP's array_map() to do this
function cb($obj) { return $obj->myMethod(); }
.
.
$data = array_map(cb, $objects);
If you have php 5.3 or better, the neater way is to use array_map() with anonymous functions:
$retArray = array_map(function($o){ $o->myMethod(); }, $myArray);
If you don't have php 5.3, you are left with having to declare the function before hand and passing the function name to array_map()
You could use a bit more OOP:ish approach using iterators.
class TrainedDogIterator implements Iterator {
// implement methods on http://php.net/iterator
public function current() {
$dog = current($this->dogs); // $this->dogs would be your objects
$trained_dog = $this->_trainDog($dog);
return $trained_dog;
}
private function _trainDog($dog) {
// do something with dog
return $dog;
}
}
Use it where you would use $data in your example.
$di = new TrainedDogIterator($dogs);
foreach($di as $dog) {
// $dog is trained
}
TMTOWTDI, array_walk works similarly to array_map, but modifies the array in-place. Though array_map has the advantage of supporting multiple arrays:
http://us3.php.net/array_walk