PHP function to validate array - php

Is there a function which I could give an array, which would return true if the provided function returned true for all of them?
theFunction(array(1,2,3) , 'is_numeric') //true
theFunction(array(1,"b",3) , 'is_numeric') //false

No, but you can use array_reduce:
array_reduce(array(1,2,3),
function ($a, $v) { return $a && is_numeric($v); }, true);
You can of course build your own higher-order function:
function for_all(array $arr, $func) {
return array_reduce($arr,
function ($a, $v) use ($func) {
return $a && call_user_func($func, $v);
}, true);
}
var_dump(
for_all(array(1,2,3), 'is_numeric')
); //true

array_filter() does the job:
$data = array(1, 2, 3);
if ($data === array_filter($data, 'is_numeric'))
{
// all values of $data are numeric
}

/**
* all in collection?
*
* Passes each element of the collection to the given function. The method
* returns true if the function never returns false or null.
*
* If the function is not given, an implicit
* function ($v) { return ($v !== null && $v !== false) is added
* (that is all() will return true only if none of the collection members are false or null.)
*
* #param array $arr input array
* #param function $lambda takes an element, returns a bool (optional)
* #return boolean
*/
function all($arr, $lambda=null) {
// these differ from PHP's "falsy" values
if (!is_callable($lambda)) {
foreach ($arr as $value)
if ($value === false || $value === null)
return false;
} else {
foreach ($arr as $value)
if (!call_user_func($lambda, $value))
return false;
}
return true;
}
This is lifted from my implementation of Ruby's enum
You can call it like:
var_dump(all($array, 'is_numeric'));
var_dump(all($array, 'is_string'));
var_dump(all($array, function($x) { return $x != 'fun';})); // PHP >= 5.3.0

If you don't mind about efficiency and care more about simplicity you can use the min and array_map without having to create new functions.
(bool)min(array_map('is_numeric', array(1,2,3))); //true
(bool)min(array_map('is_numeric', array(1,"b",3))); //false
Also if you think about the process as finding one that doesn't fit the pattern you can rewrite it a bit cleaner.
!array_filter('is_not_numeric', array(1,2,3)); //true
!array_filter('is_not_numeric', array(1,"b",3)); //true

This is the function to validate value against validation rules or just use callables: names of PHP functions or closures.
/**
* Returns true if $value matches $pattern
*
* #param $value
* #param string $pattern
*
* #return bool
*
* #see https://github.com/ptrofimov/matchmaker - ultra-fresh PHP matching functions
* #author Petr Trofimov <petrofimov#yandex.ru>
*/
function matcher($value, $pattern)
{
$args = [];
if (($p = ltrim($pattern, ':')) != $pattern) foreach (explode(' ', $p) as $name) {
if (substr($name, -1) == ')') {
list($name, $args) = explode('(', $name);
$args = explode(',', rtrim($args, ')'));
}
if (is_callable(rules($name))) {
if (!call_user_func_array(rules($name), array_merge([$value], $args))) {
return false;
}
} elseif (rules($name) !== $value) {
return false;
}
} else {
return $pattern === '' || $value === $pattern;
}
return true;
}
You can use it with prepared set of validation rules, implemented in project matchmaker: https://github.com/ptrofimov/matchmaker

Related

Adding an alphabetic sort order

I have some code that I have tried modifying so that the array is sorted alphabetically, I thought the following:
public function getOptions(array $aggs, $filterEmptyOptions = true)
Should be:
public function getOptions(array sort($aggs), $filterEmptyOptions = true)
But it doesn't work, where should I add sort() to the array?
EDIT: I am trying to sort the brewery names alphabetically
This is my full code if it makes it easier?
class BreweryBoolMustFilter implements BoolFilterInterface
{
/**
* #param Query $query
* #param Query\BoolQuery $bool
* #param Request $request
* #param array $filters
*/
public function __construct(Query $query, Query\BoolQuery $bool, Request $request, array &$filters)
{
if ($request->query->has('brewery') && (int) $request->query->get('brewery'))
{
$filters['brewery'] = (int) $request->query->get('brewery');
$bool->addMust(new Query\Term(['breweryId' => $filters['brewery']]));
}
$aggs3 = new Terms('breweries');
$aggs3->setField('breweryId');
$aggs3->setSize(100);
$query->addAggregation($aggs3);
}
/**
* #param array $aggs
* #param bool $filterEmptyOptions
*
* #return array
*/
public function getOptions(array $aggs, $filterEmptyOptions = true)
{
$breweriesObjects = (new BreweryQuery)->find();
$breweries = [];
foreach ($breweriesObjects as $breweryObject)
{
$breweries[$breweryObject->getId()] = [
'name' => $breweryObject->getName(),
'doc_count' => 0,
];
}
foreach ($aggs['breweries']['buckets'] as $bucket)
{
if (isset($breweries[$bucket['key']]))
{
$breweries[$bucket['key']]['doc_count'] = $bucket['doc_count'];
}
}
if (true === $filterEmptyOptions)
{
foreach ($breweries as $key => $brewery)
{
if (0 == $brewery['doc_count'])
{
unset($breweries[$key]);
}
}
}
return $breweries;
}
PHP indexes only natural datatypes, you can't execute a function with an argument.
The best case you can do to close the scope is put inside the function or work with anonymous functions to close scope in all your code:
$f = function($data){
return sort($data);
};
yourfunction($f());
yourfunction2($f());
You need to call sort() in the function, not in the parameter list. You should be sorting $breweries after you fill it in, sorting it by the name values. You can use usort() for this.
public function getOptions(array $aggs, $filterEmptyOptions = true)
{
$breweriesObjects = (new BreweryQuery)->find();
$breweries = [];
foreach ($breweriesObjects as $breweryObject)
{
$breweries[$breweryObject->getId()] = [
'name' => $breweryObject->getName(),
'doc_count' => 0,
];
}
usort($breweries, function($a, $b) {
if ($a['name'] < $b['name']) {
return -1;
} elseif ($a['name'] > $b['name']) {
return 1;
} else {
return 0;
}
});
foreach ($aggs['breweries']['buckets'] as $bucket)
{
if (isset($breweries[$bucket['key']]))
{
$breweries[$bucket['key']]['doc_count'] = $bucket['doc_count'];
}
}
if (true === $filterEmptyOptions)
{
foreach ($breweries as $key => $brewery)
{
if (0 == $brewery['doc_count'])
{
unset($breweries[$key]);
}
}
}
return $breweries;
}

Elegant way to search an PHP array using a user-defined function

Basically, I want to be able to get the functionality of C++'s find_if(), Smalltalk's detect: etc.:
// would return the element or null
check_in_array($myArray, function($element) { return $elemnt->foo() > 10; });
But I don't know of any PHP function which does this. One "approximation" I came up with:
$check = array_filter($myArray, function($element) { ... });
if ($check)
//...
The downside of this is that the code's purpose is not immediately clear. Also, it won't stop iterating over the array even if the element was found, although this is more of a nitpick (if the data set is large enough to cause problems, linear search won't be an answer anyway)
To pull the first one from the array, or return false:
current(array_filter($myArray, function($element) { ... }))
More info on current() here.
Here's a basic solution
function array_find($xs, $f) {
foreach ($xs as $x) {
if (call_user_func($f, $x) === true)
return $x;
}
return null;
}
array_find([1,2,3,4,5,6], function($x) { return $x > 4; }); // 5
array_find([1,2,3,4,5,6], function($x) { return $x > 10; }); // null
In the event $f($x) returns true, the loop short circuits and $x is immediately returned. Compared to array_filter, this is better for our use case because array_find does not have to continue iterating after the first positive match has been found.
In the event the callback never returns true, a value of null is returned.
Note, I used call_user_func($f, $x) instead of just calling $f($x). This is appropriate here because it allows you to use any compatible callable
Class Foo {
static private $data = 'z';
static public function match($x) {
return $x === self::$data;
}
}
array_find(['x', 'y', 'z', 1, 2, 3], ['Foo', 'match']); // 'z'
Of course it works for more complex data structures too
$data = [
(object) ['id' => 1, 'value' => 'x'],
(object) ['id' => 2, 'value' => 'y'],
(object) ['id' => 3, 'value' => 'z']
];
array_find($data, function($x) { return $x->id === 3; });
// stdClass Object (
// [id] => 3
// [value] => z
// )
If you're using PHP 7, add some type hints
function array_find(array $xs, callable $f) { ...
The original array_search returns the key of the matched value, and not the value itself (this might be useful if you're will to change the original array later).
try this function (it also works will associatives arrays)
function array_search_func(array $arr, $func)
{
foreach ($arr as $key => $v)
if ($func($v))
return $key;
return false;
}
Use \iter\search() from nikic's iter library of primitive iteration functions. It has the added benefit that it operates on both arrays and Traversable collections.
$foundItem = \iter\search(function ($item) {
return $item > 10;
}, range(1, 20));
if ($foundItem !== null) {
echo $foundItem; // 11
}
Pulled from Laravel's Illuminate\Collections\Arr::first method:
if (!function_exists('array_first')) {
/**
* Return the first element in an array passing a given truth test.
*
* #param iterable $array
* #param callable|null $callback
* #param mixed $default
* #return mixed
*/
function array_first($array, callable $callback = null, $default = null)
{
if (is_null($callback)) {
if (empty($array)) {
return $default;
}
foreach ($array as $item) {
return $item;
}
}
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
return $value;
}
}
return $default;
}
}
I think it's pretty good. There is also the Illuminate\Collections\Arr::last method, but it's probably not as optimized since it reverses the array and just calls the first method. It does get the job done, though.
if (!function_exists('array_last')) {
/**
* Return the last element in an array passing a given truth test.
*
* #param array $array
* #param callable|null $callback
* #param mixed $default
* #return mixed
*/
function array_last($array, callable $callback = null, $default = null)
{
if (is_null($callback)) {
return empty($array) ? $default : end($array);
}
return array_first(array_reverse($array, true), $callback, $default);
}
}
Pro-tip: If you have an array of objects, then you can specify the type of the callback's argument for that sweet IDE auto-completion.
$john = array_first($users, function(User $user) {
return $user->name === 'John';
});
// Works with pretty much anything.
$activeUsers = array_filter($users, function(User $user) {
return $user->isActive;
});
// Class example:
class User {
public string $name;
public bool $isActive;
//...
}
If you want to use some variable from the outer scope, you can use the use(&$var) syntax like this
foreach($values as $key => $value) {
array_find($conditionsRecords, function($row) use(&$key) {
$keyToFind = $key;
return $keyToFind;
})
}
You can write such a function yourself, although it is little more than a loop.
For instance, this function allows you to pass a callback function. The callback can either return 0 or a value. The callback I specify returns the integer if it is > 10. The function stops when the callback returns a non null value.
function check_in_array(array $array, $callback)
{
foreach($array as $item)
{
$value = call_user_func($callback, $item);
if ($value !== null)
return $value;
}
}
$a = array(1, 2, 3, 6, 9, 11, 15);
echo check_in_array($a, function($i){ return ($i > 10?$i:null); });
You can write your own function ;)
function callback_search ($array, $callback) { // name may vary
return array_filter($array, $callback);
}
This maybe seems useless, but it increases semantics and can increase readability

Filter ArrayObject (PHP)

I have my data in ArrayObject, simply representing an array. I need to filter the data, function array_filter() would work great. However, it does not work with ArrayObject as argument. What's the best way to treat with this? Is there any standard function which handles filtering for me?
Example:
$my_data = ArrayObject(array(1,2,3));
$result = array_object_filter($my_data, function($item) { return $item !== 2; });
Is there any array_object_filter function?
How about you export it to an actual array, and then create a new Array Object?
$my_data = new ArrayObject(array(1,2,3));
$result = new ArrayObject(
array_filter( (array) $my_data, function($item) {
return $item !== 2;
})
);
How about subclassing the ArrayObject and adding a new method to it:
/**
* Filters elements using a callback function.
*
* #param callable $callback The callback function to use
*
* #return self
*/
public function filter(/* callable */ $callback = null)
{
$this->exchangeArray(array_filter($this->getArrayCopy(), $callback));
return $this;
}
How about this:
$my_data = new ArrayObject(array(1,2,3));
$callback = function($item) { return $item !== 2; };
$result = new ArrayObject;
foreach ($my_data as $k => $item) if ($callback($item)) $result[$k] = $item;
Alternately, you can define an array_object_filter() function yourself:
function array_object_filter($array, $callback) {
$result = new ArrayObject;
foreach ($array as $k => $item) if ($callback($item)) $result[$k] = $item;
return $result;
}

array_walk_recursive - modify both keys and values

How can you modify both keys and values with array_walk_recursive??
Here only the values are encoded
function _utf8_encode($arr){
array_walk_recursive($arr, 'utf8_enc');
return $arr;
}
function utf8_enc(&$value, &$key){
$value = utf8_encode($value);
$key = utf8_encode($key);
}
array_walk_recursive does ONLY apply the user function on the VALUES of an array, not on indexes (I think it has something to with the fact, that the indexes of an array have to be unique, so you cannot manipulate them). Best thing would be to write a recursive function on yourself. The following should work:
function utf8enc($array) {
if (!is_array($array)) return;
$helper = array();
foreach ($array as $key => $value) $helper[utf8_encode($key)] = is_array($value) ? utf8enc($value) : utf8_encode($value);
return $helper;
}
$enc_array = utf8enc($your_array);
This my recursive function that can change not only the values of the array as array_walk_recursive() but also the keys of the given array. It also keeps the order of the array.
/**
* Change values and keys in the given array recursively keeping the array order.
*
* #param array $_array The original array.
* #param callable $_callback The callback function takes 2 parameters (key, value)
* and returns an array [newKey, newValue] or null if nothing has been changed.
*
* #return void
*/
function modifyArrayRecursive(array &$_array, callable $_callback): void
{
$keys = \array_keys($_array);
foreach ($keys as $keyIndex => $key) {
$value = &$_array[$key];
if (\is_array($value)) {
modifyArrayRecursive($value, $_callback);
continue;
}
$newKey = $key;
$newValue = $value;
$newPair = $_callback ($key, $value);
if ($newPair !== null) {
[$newKey, $newValue] = $newPair;
}
$keys[$keyIndex] = $newKey;
$_array[$key] = $newValue;
}
$_array = \array_combine($keys, $_array);
}
/**
* Usage example
*/
modifyArrayRecursive($keyboardArr, function ($key, $value) {
if ($value === 'some value') {
return ['new_key_for_this_value', $value];
}
return null;
});
Another recursive function in addition to rabudde's answer:
function utf8_encode_array($array_to_encode=array()){
$encoded_array=array();
if(is_array($array_to_encode)){
foreach($array_to_encode as $key => $value){
$key=utf8_encode($key);
if(is_array($value)){
$encoded_array[$key]=utf8_encode_array($value);
}
else{
$encoded_array[$key]=utf8_encode($value);
}
}
}
return $encoded_array;
}
/**
* Recursively modifies keys or/and values of an array
* #param array $array
* #param callable $func
* #return array
*/
function modifyArrayRecursive(array $array, callable $func): array
{
$nArray = [];
foreach($array as $key => $value) {
if(is_array($value)) {
$value = modifyArrayRecursive($value, $func);
}
$nArray += $func($key, $value);
}
return $nArray;
}
/**
* Array wants to be modified
*/
$array = [
'key1' => [
'key2' => ['value1', 'value2']
],
'value3',
'value4'
];
/**
* Example of getting new modified array
*/
$nArray = modifyArrayRecursive($array, function($key, $value) {
if($key === 'key2') {
$key = 'key2_upd';
}
if($value === 'value4') {
$value = 'value4_upd';
}
return [$key => $value];
});

making PHP join/concat (.) like JavaScript

I've stuck on a seems-to-be simple command join, but can't work it out.
I have a between() function which does the following:
/**
* Checks if passed int is between $left and $right
* #param int $left lowest value
* #param int $right highest value
* #param int $value actual value
* #return bool is $value between $left and $right
*/
function between($left, $right, $value)
{
$value = intval($value);
return ( $value >= $left && $value <= $right );
}
and the usage is pretty simple:
$int = 9;
var_dump( between( 6, 14, $int ) );//bool(true)
Now what I want to achieve is:
$int = 9;
var_dump( $int.between( 6, 14 ) );//bool(true)
it would make more sense and would be easier to understand.
Any ideas how do I achieve this?
If $int would be an object which extends comparisonFunctions I could do $int->between(); but maybe there is a way to catch what does . join?
Thanks in advance
The . operator has a different meaning in Javascript and in PHP: In Javascript it is used for property accessor while PHP uses it for string concatenation. For property access in PHP you use the -> operator on objects (and the :: operator on classes) instead.
So to get the same behavior you would need to have an object value with such a method instead of a scalar value:
class Integer {
private $value;
public function __constructor($value) {
$this->value = intval($value);
}
public function between($min, $max) {
if (!($min instanceof Integer)) {
$min = new Integer($min);
}
if (!($max instanceof Integer)) {
$max = new Integer($max);
}
return $min->intValue() <= $this->value && $this->value <= $max->intValue();
}
public function intValue() {
return $this->value;
}
}
Then you can do this:
$int = new Integer(9);
var_dump($int->between(6, 14));
But maybe it already suffuces if you just name the function properly and switch the parameter order:
isInRange($val, $min, $max)
$int is of the primitive type int and contains the value 9. It is not an object that has instance methods/functions. This (sadly) isn't Ruby ;)
What you want isn't possible in PHP unless you do something like this - but I wouldn't advise it:
class Integer {
private $value;
public function __construct($value) {
$this->setValue((int)$value);
}
public function getValue() {
return $this->value;
}
public function setValue($value) {
$this->value = $value;
}
public function between($a, $b) {
return ($this->getValue() >= $a && $this->getValue() <= $b);
}
}

Categories