Notes on codes, projects and everything

Mixing Non-array and Array in array_map()

array_map function is a function that I use the most in my php scripts recently. However, there are times where I want to pass some non-array into it, therefore often times I have code like the snippet shown below:

$result = array_map(
'some_callback',
array_fill(0, count($some_array), 'some_string'),
array_fill(0, count($some_array), 'some_other_string'),
$some_array
)

It doesn’t look good IMO, as it makes the code looks complicated. Hence, after seeing how the code may vary in all different scenarios, I created some functions to clean up the array_map call as seen above. Code snippet after the jump

function array_map_mix($callback, $param) {
$main = array();

$args = func_get_args();

if((is_numeric($param) && count($args) < 3) || (is_array($param) && count($args) < 2)) { throw new Exception('No input available.'); } elseif(is_array($param)) { list($main, $param) = array($param, count($param)); } elseif(is_numeric($param) === FALSE) { throw new Exception('Invalid parameter'); } return call_user_func_array( 'array_map', array_merge( array($callback), count($main) > 0 ? array($main) : array(),
count($args) - 2 > 0 ?
array_map(
'array_create',
array_create(count($args) - 2, $param),
array_slice($args, 2)
)
: array()
)
);
}

function array_create($count, $input) {
$result = array_fill(0, $count, NULL);

if(is_array($input) && count($input) == 1 && is_array($input[0])) {
$result = array_fill(0, $count, $input[0]);
} else if(is_array($input) && count($input) == $count) {
$result = $input;
} else {
$result = array_fill(0, $count, $input);
}

return $result;
}

array_create

Before stepping into the main array_map_mix function, we first look at the array_create function. It is basically a variant of array_fill function, but without the initial index argument.

The important argument here is the $input argument, depending on what is passed into the function, the end result can be different. If anything but array is passed in, then it will behave exactly like the array_fill call as follows:

$foo = array_create(5, 'bar');
// is equivalent to
$foo = array_fill(0, 5, 'bar');

However, if an array is passed in, and the array element count is equivalent to the $count argument, then array_create will return $input without any modification.

array('bar', 'baz') == array_create(2, array('bar', 'baz'));

If the array passed in has one and only one element, and the element happens to be another array, then the child array is passed into the array_fill function, as follows

$foo = array_create(3, array(array('bar', 'baz')));
// is equivalent to
$foo = array_fill(0, 3, array('bar', 'baz'))

If the array passed in doesn’t fulfill the above two conditions, then the end result is similar like passing in a non-array argument.

$foo = array_create(3, array('bar'));
// is equivalent to
$foo = array_fill(0, 3, array('bar'));

The weird behavior caused by the array argument is actually intended, as this function is initially created for the following function, which is the array_map_mix function.

array_map_mix

The first 2 mandatory arguments for array_map_mix are the $callback as well as $param. The $callback is the callback function, and $param is the parameter. The parameter can be either a number, or an array.

When a number is passed in as the parameter, then the code is throwing all the following arguments into array_create function as described above, with the number is sent in as $count. This is an example showing the equivalent array_map function call.

$foo = array_map_mix(
'some_callback',
2,
array('foo', 'bar'),
'baz'
);
// is equivalent to
$foo = array_map(
'some_callback',
array('foo', 'bar'),
array_fill(0, 2, 'baz')
);

What if the number passed in does not equal to the count of array elements?

$foo = array_map_mix(
'some_callback',
2,
array('foo', 'bar', 'baz')
);
// is equivalent to
$foo = array_map(
'some_callback',
array_fill(0, 2, array('foo', 'bar', 'baz'))
);

What if I have 2 array, $foo and $bar, even both has the same amount of array elements, but I want the callback to receive $bar as a whole, instead of just an element?

// given
count($foo) == count($bar);
// wants this
$foo = array_map(
'some_callback',
$foo,
array_fill(0, count($foo), $bar)
);
// equivalent array_map_mix call
$foo = array_map_mix(
'some_callback',
count($foo),
$foo,
array($bar)
);

Hopefully this explains why array_create is coded like above. As mentioned above, $param can be an array as well. This is helpful when the count of array to be passed to array_map is dynamic and a fixed number is not always available.

// suppose function foo() returns an array of variable size
$foo = array_map_mix(
'some_callback',
foo(),
'baz'
);
// the equivalent code
$bar = foo();
$foo = array_map(
'some_callback',
$bar,
array_fill(0, count($bar), 'baz')
);

Nevertheless, array_map_mix can also be used as a direct array_map alternative, although this is not advisible as the function call should take much longer time to complete because of the call_user_func_array call within array_map_mix.

array_map('some_callback', array('foo', 'bar')) == array_map_mix('some_callback', array('foo', 'bar'));

The total number of arguments required is 2 when $param is an array (means using array_map_mix as array_map) and 3 when $param is a non-array. An exception will be thrown if expected argument is missing. Also, if anything but an array or a number is passed into array_map_mix as $param, then another exception is thrown.

Hopefully these two short code snippet helps for people who uses array_map as much as I do.

leave your comment

name is required

email is required

have a blog?

This blog uses scripts to assist and automate comment moderation, and the author of this blog post does not hold responsibility in the content of posted comments. Please note that activities such as flaming, ungrounded accusations as well as spamming will not be entertained.

Click to change color scheme