For a long time now, and like pretty much every other PHP dev out there, at some point I invented my own Object wrapper around arrays to give them nice OOP interfaces. I am a low level person so implementing my own wrappers is far more entertaining to me than just using one of the 34859 that already exist. Mine ended up in a library called
Nether\Object that provides a few utilities for things I found myself doing too often making arrays into objects and maybe even back again.
The class in question though Nether\Object\Datastore is
really just a fancy wrapper to handle arrays without having to remember
if the array comes first or second in the argument lists with the
various array_ functions.
Lately I have been doing a lot of callable programming, which while not an official term is where I would be using array_filter or array_map supplied with an anonymous function to iterate over an array instead of looping structures*. These two functions in particular are super annoying as array_filter expects the array to be the second arg, while array_map expects it to be the first. And then if you start nesting these calls together you get some shitty looking code.
(*You can't call it map->reduce when the actual common use is reduce->map and also it's not even using reduce just a general reducer. Why would you map an entire large dataset before reducing so you could map a smaller dataset get serious now.)
A recent specific case was I wanted a list of Blogs that a User has management privileges for. I never actually used this code for more than a test or two, but I did slowly iterate into it until realizing exactly how much I hated everything that lead up to this moment.
$Blogs = array_map(
function(BlogUser $Val){ return $Val->Blog; },
array_filter(
BlogUser::Find([ 'UserID' => $this->User->ID ])->Payload->GetData(),
function(BlogUser $Val) { return $Val->HasManagePriv(); }
)
);
First BlogUser::Find returns a SearchResult and Payload is one of my Datastore objects, which implements all the various iterable things needed to make the object... iterable. But the array_ functions refuse to take them, so then I ended up doing the GetData for the raw array. At this point I am already annoyed, I should just rewrite this with a foreach() so my Datastore can do its things. It was only then it hit me that, durr, maybe just build array_filter into the Datastore, as it already had a Map method.
Ended up implementing two versions, Filter, which will edit the data inside directly, and Distill, which performs the same filtering mechanic but returns a new Datastore object leaving the original untouched.
public function
Distill(Callable $FilterFunc):
self {
/*//
@date 2020-10-22
return a new datastore of the result of an array filter.
//*/
return new static(array_filter($this->Data,$FilterFunc));
}
public function
Filter(Callable $FilterFunc):
self {
/*//
@date 2020-10-22
alter the current dataset with the result of an array filter.
//*/
$this->Data = array_filter($this->Data,$FilterFunc);
return $this;
}
This ended up turning my original cluster into something slightly more manageable. The final result is instead of an array of blogs, I actually got a filtered Datastore of them and this was more than fine to me.
$Blogs = (
(BlogUser::Find([ 'UserID' => $this->User->ID ])->Payload)
->Filter(function(BlogUser $Val){ return $Val->HasManagePriv(); })
->Remap(function(BlogUser $Val){ return $Val->Blog; })
);
Additional background info, just like how Distill returns a new Datastore and Filter modifies the original, the Map method returns a new Datastore and Remap alters the original. I implemented both for cases where maybe you actually need to keep your original list around.
If you are interested in playing with mine specifically do a `composer require netherphp/object` and pop up a $Data = new Nether\Object\Datastore($ArrayOfCrap); and give some of the methods a spin.