Related
Let's say I have following arrays:
$a = [1,2,3,4,5];
$b = [1,3,4,5,6];
$c = [1,7,8,9,10];
$d = [1,2,3,4];
The intersection of those would be $result = [1], which is easy enough. But what if I wanted the intersection of those with a minimum threshold of let's say 3? The threshold means I can skip one or more arrays from the intersection, as long as my resulting intersection has at least 3 elements, which in this case might result in:
$result = [1,3,4];
1, 3 and 4 are present in $a, $b and $d, but not in $c which is skipped because of the threshold. Is there an existing PHP class, algorithm or function with which I might accomplish this?
To do that we have to use combinations of an array. I have used combinations algorithm from this great article. Adjusting this algorithm we can write the following class:
class Intersections
{
protected $arrays;
private $arraysSize;
public function __construct($arrays)
{
$this->arrays = $arrays;
$this->arraysSize = count($arrays);
}
public function getByThreshold($threshold)
{
$intersections = $this->getAll();
foreach ($intersections as $intersection) {
if (count($intersection) >= $threshold) {
return $intersection;
}
}
return null;
}
protected $intersections;
public function getAll()
{
if (is_null($this->intersections)) {
$this->generateIntersections();
}
return $this->intersections;
}
private function generateIntersections()
{
$this->generateCombinationsMasks();
$this->generateCombinations();
$combinationSize = $this->arraysSize;
$intersectionSize = 0;
foreach ($this->combinations as $combination) {
$intersection = call_user_func_array('array_intersect', $combination);
if ($combinationSize > count($combination)) {
$combinationSize = count($combination);
$intersectionSize = 0;
}
if (count($intersection) > $intersectionSize) {
$this->intersections[$combinationSize] = $intersection;
$intersectionSize = count($intersection);
}
}
}
private $combinationsMasks;
private function generateCombinationsMasks()
{
$combinationsMasks = [];
$totalNumberOfCombinations = pow(2, $this->arraysSize);
for ($i = $totalNumberOfCombinations - 1; $i > 0; $i--) {
$combinationsMasks[] = str_pad(
decbin($i), $this->arraysSize, '0', STR_PAD_LEFT
);
}
usort($combinationsMasks, function ($a, $b) {
return strcmp(strtr($b, ['']), strtr($a, ['']));
});
$this->combinationsMasks = array_slice(
$combinationsMasks, 0, -$this->arraysSize
);
}
private $combinations;
private function generateCombinations()
{
$this->combinations = array_map(function ($combinationMask) {
return $this->generateCombination($combinationMask);
}, $this->combinationsMasks);
}
private function generateCombination($combinationMask)
{
$combination = [];
foreach (str_split($combinationMask) as $key => $indicator) {
if ($indicator) {
$combination[] = $this->arrays[$key];
}
}
return $combination;
}
}
I have tried to give self-explanatory names to methods. Some chunks of code can be optimized more (for example, I call count function multiple times on same arrays; this was done in order to reduce variables fiddling) for production use.
So basically the logic is pretty simple. We generate all combinations of arrays and sort them decreasingly by the number of used arrays. Then we find the longest intersection for each length of combinations. Actually, this is the hardest part. To get one particular intersection we return first one that matches threshold.
$intersections = new Intersections([$a, $b, $c, $d]);
var_dump($intersections->getAll());
var_dump($intersections->getByThreshold(3));
Here is working demo.
There are other ways to find all combinations, for example, one from "PHP Cookbook". You can choose whatever one you like most.
No build in feature for that. You need to write something short like:
$values = [];
foreach ([$a, $b, $c, $d] as $arr)
foreach ($arr as $value)
$values[$value] = ($values[$value] ?? 0) + 1;
// For threshold of 3
$values = array_keys(array_filter($values, function($a) { return $a >= 3; }));
Note: This requires PHP7 for ?? operator. Otherwise use something like:
$values[$value] = empty($values[$value]) ? 1 : $values[$value] + 1;
I have strict error reporting. I have to use isset and it make me to write long, repetitive chains of variables in PHP. I have sometimes to write code like this:
if (isset($my_object->an_array[$a_variable])):
$other_variable = $my_object->an_array[$a_variable];
else:
$other_variable = false;
endif;
or
if (isset($my_object->an_array[$a_variable])):
return $my_object->an_array[$a_variable];
endif;
Sometimes it is longer and more complicated. It isn't readable and take too much time to type. I'd like to get rid of it.
The question
Is there a way to write $my_object->an_array[$a_variable] only once?
You can write functions to encapsulate repetitive code:
function get_variable(array $array, $variable_name, $default_value=FALSE){
if( isset($array[$variable_name]) ){
return $array[$variable_name];
}else{
return $default_value;
}
}
Tweak to your needs.
In the end I have found two solutions.
I. There is operator # in PHP. It is very dangerous, tough.
http://www.php.net/manual/en/language.operators.errorcontrol.php
However, it is acceptable in my situation.
This is not a fatal error.
The value of undefined variable is defined as null. I'm fine with testing for this or using implicit conversions.
I can use $php_errormsg in extreme situations.
The code example:
$tmp = $false_object->property->property; #Throw notice
$tmp = $false_array['a_field']['a_field']; #Throw notice
$tmp = #$false_object->property->property; #Quiet
$tmp = #$false_array['a_field']['a_field']; #Quiet
echo $php_errormsg; #I can print that notice
The downside is I don't receive information about lack of quotes in brackets.
$a = array('e'=>false);
$tmp = $a[e]; #Throw notice
$tmp = #$a[e]; #Quiet
echo $php_errormsg; #This variable still works
II. It is possible to use operator &.
The value of undefined variable will be NULL too.
The $php_errormsg variable doesn't work for undefined variables.
I get notice for lack of quotes in brackets, though.
The code example:
$tmp = $false_object->property->property; #Throw notice
$tmp = $false_array['a_field']['a_field']; #Throw notice
$tmp = &$false_object->property->property; #Quiet
$tmp = &$false_array['a_field']['a_field']; #Quiet
var_dump($tmp); #NULL;
The lack of quotes problem:
$array = array('a_field'=>true);
$tmp = $array[a_field]; #Throw notice
$tmp = #$array[a_field]; #Quiet
$tmp = &$array[a_field]; #Throw notice
function check($var)
{
if(isset[$var])
return $var;
else
return "";
}
Then each time you need to do checking call like:
$other_b = check($b);
I doubt you will get any suggestions that you will consider satisfactory. The best I can suggest is this, and I would add that I consider it quite ugly:
function ifset ($var) {
return is_null($var) ? false : $var;
}
Having defined this function, you can call it like this:
$other_variable = ifset(#$my_object->an_array[$a_variable]);
Note that you need the error suppression operator here, because otherwise you'll get an undefined variable notice if the variable indeed doesn't exist. (The reason why you don't need it for isset() is that isset() is really a special parser token rather than an ordinary function.)
now i get the same problem. i must check it and then get it ,it's so ugrly.
so i write the function like this
function get_val($arr,$key,$default_val=false){
if(!is_array($arr)) return $default_val;
$idx = explode('>>',$key);
$tmp = $arr;
$catched = true;
foreach($idx as $index) {
if(!isset($tmp[$index])){
$catched = false;
break;
}else{
$tmp = $tmp[$index];
}
}
if($catched) $default_val = $tmp;
return $default_val;
}
//for example
$arr = array('k1'=>array('k2'=>array(1,'k22'=>22,'k23'=>array('k3'=>1))));
get_val($arr,'k1>>k2>>k23>>k3');
A method to extract those variables would probably be better in your case, then:
class MyObject
{
private $an_array;
public function __construct()
{
$this->an_array = array();
}
public function get( $key )
{
if(isset($this->an_array[$key]))
return $this->an_array[$key];
return false; //or empty string
}
public function set( $key, $value )
{
$this->an_array[$key] = $value;
}
}
That way, you can do it like this:
$my_object->get($a_variable]);
I use these little helper functions to access properties of (multidimensional) arrays/objects without writing repetitive isset() statements. They might not be the fastest running solution, but they are very comfortable:
// AI(data,1,default) === data[1] or default
function AI( $a, $i, $d=null ){
if( is_array ($a)){ if( array_key_exists( $i, $a )) return $a[ $i ]; return $d; }
if( is_object($a)){ if( property_exists( $a, $i )) return $a -> $i; return $d; }
return $d;
}
// AII(data,1,2,3) === data[1][2][3] or null
function AII( $o ){
$a = func_get_args();
$al = func_num_args();
for( $i=1; $i < $al; $i++ ){
$k = $a[$i];
if( is_array ($o) && array_key_exists($k,$o)) $o =& $o[ $k ];
else if( is_object($o) && property_exists ($o,$k)) $o =& $o -> $k;
else return null; // nothing to access
}
return $o;
}
// AIID(data,1,2,3,default) == data[1][2][3] or default
function AIID( $o ){
$a = func_get_args();
$default = end( $a );
$al = count( $a ) - 1;
for( $i=1; $i < $al; $i++ ){
$k = $a[$i];
if( is_array ($o) && array_key_exists($k,$o)) $o =& $o[ $k ];
else if( is_object($o) && property_exists ($o,$k)) $o =& $o -> $k;
else return $default;
}
return $o;
}
// AAID(data,[1,2,3],default) == data[1][2][3] or default
function AAID( $o, $a, $default = null ){
foreach( $a as $k ){
if( is_array ($o) && array_key_exists($k,$o)) $o =& $o[ $k ];
else if( is_object($o) && property_exists ($o,$k)) $o =& $o -> $k;
else return $default;
}
return $o;
}
What would be the fastest, most efficient way to implement a search method that will return an object with a qualifying id?
Sample object array:
$array = [
(object) ['id' => 'one', 'color' => 'white'],
(object) ['id' => 'two', 'color' => 'red'],
(object) ['id' => 'three', 'color' => 'blue']
];
What do I write inside of:
function findObjectById($id){
}
The desired result would return the object at $array[0] if I called:
$obj = findObjectById('one')
Otherwise, it would return false if I passed 'four' as the parameter.
You can iterate that objects:
function findObjectById($id){
$array = array( /* your array of objects */ );
foreach ( $array as $element ) {
if ( $id == $element->id ) {
return $element;
}
}
return false;
}
Edit:
Faster way is to have an array with keys equals to objects' ids (if unique);
Then you can build your function as follow:
function findObjectById($id){
$array = array( /* your array of objects with ids as keys */ );
if ( isset( $array[$id] ) ) {
return $array[$id];
}
return false;
}
It's an old question but for the canonical reference as it was missing in the pure form:
$obj = array_column($array, null, 'id')['one'] ?? false;
The false is per the questions requirement to return false. It represents the non-matching value, e.g. you can make it null for example as an alternative suggestion.
This works transparently since PHP 7.0. In case you (still) have an older version, there are user-space implementations of it that can be used as a drop-in replacement.
However array_column also means to copy a whole array. This might not be wanted.
Instead it could be used to index the array and then map over with array_flip:
$index = array_column($array, 'id');
$map = array_flip($index);
$obj = $array[$map['one'] ?? null] ?? false;
On the index the search problem might still be the same, the map just offers the index in the original array so there is a reference system.
Keep in mind thought that this might not be necessary as PHP has copy-on-write. So there might be less duplication as intentionally thought. So this is to show some options.
Another option is to go through the whole array and unless the object is already found, check for a match. One way to do this is with array_reduce:
$obj = array_reduce($array, static function ($carry, $item) {
return $carry === false && $item->id === 'one' ? $item : $carry;
}, false);
This variant again is with the returning false requirement for no-match.
It is a bit more straight forward with null:
$obj = array_reduce($array, static function ($carry, $item) {
return $carry ?? ($item->id === 'one' ? $item : $carry);
}, null);
And a different no-match requirement can then be added with $obj = ...) ?? false; for example.
Fully exposing to foreach within a function of its own even has the benefit to directly exit on match:
$result = null;
foreach ($array as $object) {
if ($object->id === 'one') {
$result = $object;
break;
}
}
unset($object);
$obj = $result ?? false;
This is effectively the original answer by hsz, which shows how universally it can be applied.
You can use the function array_search of php like this
$key=array_search("one", array_column(json_decode(json_encode($array),TRUE), 'color'));
var_dump($array[$key]);
i: is the index of item in array
1: is the property value looking for
$arr: Array looking inside
'ID': the property key
$i = array_search(1, array_column($arr, 'ID'));
$element = ($i !== false ? $arr[$i] : null);
Well, you would would have to loop through them and check compare the ID's unless your array is sorted (by ID) in which case you can implement a searching algorithm like binary search or something of that sort to make it quicker.
My suggestion would be to first sort the arrays using a sorting algorithm (binary sort, insertion sort or quick sort) if the array is not sorted already. Then you can implement a search algorithm which should improve performance and I think that's as good as it gets.
http://www.algolist.net/Algorithms/Binary_search
This is my absolute favorite algorithm for very quickly finding what I need in a very large array, quickly. It is a Binary Search Algorithm implementation I created and use extensively in my PHP code. It hands-down beats straight-forward iterative search routines. You can vary it a multitude of ways to fit your need, but the basic algorithm remains the same.
To use it (this variation), the array must be sorted, by the index you want to find, in lowest-to-highest order.
function quick_find(&$array, $property, $value_to_find, &$first_index) {
$l = 0;
$r = count($array) - 1;
$m = 0;
while ($l <= $r) {
$m = floor(($l + $r) / 2);
if ($array[$m]->{$property} < $value_to_find) {
$l = $m + 1;
} else if ($array[$m]->{$property} > $value_to_find) {
$r = $m - 1;
} else {
$first_index = $m;
return $array[$m];
}
}
return FALSE;
}
And to test it out:
/* Define a class to put into our array of objects */
class test_object {
public $index;
public $whatever_you_want;
public function __construct( $index_to_assign ) {
$this->index = $index_to_assign;
$this->whatever_you_want = rand(1, 10000000);
}
}
/* Initialize an empty array we will fill with our objects */
$my_array = array();
/* Get a random starting index to simulate data (possibly loaded from a database) */
$my_index = rand(1256, 30000);
/* Say we are needing to locate the record with this index */
$index_to_locate = $my_index + rand(200, 30234);
/*
* Fill "$my_array()" with ONE MILLION objects of type "test_object"
*
* 1,000,000 objects may take a little bit to generate. If you don't
* feel patient, you may lower the number!
*
*/
for ($i = 0; $i < 1000000; $i++) {
$searchable_object = new test_object($my_index); // Create the object
array_push($my_array, $searchable_object); // Add it to the "$my_array" array
$my_index++; /* Increment our unique index */
}
echo "Searching array of ".count($my_array)." objects for index: " . $index_to_locate ."\n\n";
$index_found = -1; // Variable into which the array-index at which our object was found will be placed upon return of the function.
$object = quick_find($my_array, "index", $index_to_locate, $index_found);
if ($object == NULL) {
echo "Index $index_to_locate was not contained in the array.\n";
} else {
echo "Object found at index $index_found!\n";
print_r($object);
}
echo "\n\n";
Now, a few notes:
You MAY use this to find non-unique indexes; the array MUST still be sorted in ascending order. Then, when it finds an element matching your criteria, you must walk the array backwards to find the first element, or forward to find the last. It will add a few "hops" to your search, but it will still most likely be faster than iterating a large array.
For STRING indexes, you can change the arithmetic comparisons (i.e. " > " and " < " ) in quick_find() to PHP's function "strcasecmp()". Just make sure the STRING indexes are sorted the same way (for the example implementation): Alphabetically and Ascending.
And if you want to have a version that can search arrays of objects sorted in EITHER ascending OR decending order:
function quick_find_a(&$array, $property, $value_to_find, &$first_index) {
$l = 0;
$r = count($array) - 1;
$m = 0;
while ($l <= $r) {
$m = floor(($l + $r) / 2);
if ($array[$m]->{$property} < $value_to_find) {
$l = $m + 1;
} else if ($array[$m]->{$property} > $value_to_find) {
$r = $m - 1;
} else {
$first_index = $m;
return $array[$m];
}
}
return FALSE;
}
function quick_find_d(&$array, $property, $value_to_find, &$first_index) {
$l = 0;
$r = count($array) - 1;
$m = 0;
while ($l <= $r) {
$m = floor(($l + $r) / 2);
if ($value_to_find > $array[$m]->{$property}) {
$r = $m - 1;
} else if ($value_to_find < $array[$m]->{$property}) {
$l = $m + 1;
} else {
$first_index = $m;
return $array[$m];
}
}
return FALSE;
}
function quick_find(&$array, $property, $value_to_find, &$first_index) {
if ($array[0]->{$property} < $array[count($array)-1]->{$property}) {
return quick_find_a($array, $property, $value_to_find, $first_index);
} else {
return quick_find_d($array, $property, $value_to_find, $first_index);
}
}
The thing with performance of data structures is not only how to get but mostly how to store my data.
If you are free to design your array, use an associative array:
$array['one']->id = 'one';
$array['one']->color = 'white';
$array['two']->id = 'two';
$array['two']->color = 'red';
$array['three']->id = 'three';
$array['three']->color = 'blue';
Finding is then the most cheap: $one = $array['one];
UPDATE:
If you cannot modify your array constitution, you could create a separate array which maps ids to indexes. Finding an object this way does not cost any time:
$map['one'] = 0;
$map['two'] = 1;
$map['three'] = 2;
...
getObjectById() then first lookups the index of the id within the original array and secondly returns the right object:
$index = $map[$id];
return $array[$index];
Something I like to do in these situations is to create a referential array, thus avoiding having to re-copy the object but having the power to use the reference to it like the object itself.
$array['one']->id = 'one';
$array['one']->color = 'white';
$array['two']->id = 'two';
$array['two']->color = 'red';
$array['three']->id = 'three';
$array['three']->color = 'blue';
Then we can create a simple referential array:
$ref = array();
foreach ( $array as $row )
$ref[$row->id] = &$array[$row->id];
Now we can simply test if an instance exists in the array and even use it like the original object if we wanted:
if ( isset( $ref['one'] ) )
echo $ref['one']->color;
would output:
white
If the id in question did not exist, the isset() would return false, so there's no need to iterate the original object over and over looking for a value...we just use PHP's isset() function and avoid using a separate function altogether.
Please note when using references that you want use the "&" with the original array and not the iterator, so using &$row would not give you what you want.
This is definitely not efficient, O(N). But it looks sexy:
$result = array_reduce($array, function ($found, $obj) use ($id) {
return $obj['id'] == $id ? $obj : $found;
}, null);
addendum:
I see hakre already posted something akin to this.
Here is what I use. Reusable functions that loop through an array of objects. The second one allows you to retrieve a single object directly out of all matches (the first one to match criteria).
function get_objects_where($match, $objects) {
if ($match == '' || !is_array($match)) return array ();
$wanted_objects = array ();
foreach ($objects as $object) {
$wanted = false;
foreach ($match as $k => $v) {
if (is_object($object) && isset($object->$k) && $object->$k == $v) {
$wanted = true;
} else {
$wanted = false;
break;
};
};
if ($wanted) $wanted_objects[] = $object;
};
return $wanted_objects;
};
function get_object_where($match, $objects) {
if ($match == '' || !is_array($match)) return (object) array ();
$wanted_objects = get_objects_where($match, $objects);
return count($wanted_objects) > 0 ? $wanted_objects[0] : (object) array ();
};
The easiest way:
function objectToArray($obj) {
return json_decode(json_encode($obj), true);
}
Python has a nice zip() function. Is there a PHP equivalent?
As long as all the arrays are the same length, you can use array_map with null as the first argument.
array_map(null, $a, $b, $c, ...);
If some of the arrays are shorter, they will be padded with nulls to the length of the longest, unlike python where the returned result is the length of the shortest array.
array_combine comes close.
Otherwise nothing like coding it yourself:
function array_zip($a1, $a2) {
for($i = 0; $i < min(length($a1), length($a2)); $i++) {
$out[$i] = [$a1[$i], $a2[$i]];
}
return $out;
}
Try this function to create an array of arrays similar to Python’s zip:
function zip() {
$args = func_get_args();
$zipped = array();
$n = count($args);
for ($i=0; $i<$n; ++$i) {
reset($args[$i]);
}
while ($n) {
$tmp = array();
for ($i=0; $i<$n; ++$i) {
if (key($args[$i]) === null) {
break 2;
}
$tmp[] = current($args[$i]);
next($args[$i]);
}
$zipped[] = $tmp;
}
return $zipped;
}
You can pass this function as many array as you want with as many items as you want.
This works exactly as Python's zip() function, and is compatible also with PHP < 5.3:
function zip() {
$params = func_get_args();
if (count($params) === 1){ // this case could be probably cleaner
// single iterable passed
$result = array();
foreach ($params[0] as $item){
$result[] = array($item);
};
return $result;
};
$result = call_user_func_array('array_map',array_merge(array(null),$params));
$length = min(array_map('count', $params));
return array_slice($result, 0, $length);
};
It merges the arrays in the manner Python's zip() does and does not return elements found after reaching the end of the shortest array.
The following:
zip(array(1,2,3,4,5),array('a','b'));
gives the following result:
array(array(1,'a'), array(2,'b'))
and the following:
zip(array(1,2,3,4,5),array('a','b'),array('x','y','z'));
gives the following result:
array(array(1,'a','x'), array(2,'b','y'))
Check this demonstration for a proof of the above.
EDIT: Added support for receiving single argument (array_map behaves differently in that case; thanks Josiah).
Solution
The solution matching zip() very closely, and using builtin PHP functions at the same time, is:
array_slice(
array_map(null, $a, $b, $c), // zips values
0, // begins selection before first element
min(array_map('count', array($a, $b, $c))) // ends after shortest ends
);
Why not simple array_map(null, $a, $b, $c) call?
As I already mentioned in my comment, I tend to favor nabnabit's solution (array_map(null, $a, $b, ...)), but in a slightly modified way (shown above).
In general this:
array_map(null, $a, $b, $c);
is counterpart for Python's:
itertools.izip_longest(a, b, c, fillvalue=None)
(wrap it in list() if you want list instead of iterator). Because of this, it does not exactly fit the requirement to mimic zip()'s behaviour (unless all the arrays have the same length).
You can find zip as well as other Python functions in Non-standard PHP library. Including operator module and defaultarray.
use function nspl\a\zip;
$pairs = zip([1, 2, 3], ['a', 'b', 'c']);
I wrote a zip() functions for my PHP implementation of enum.
The code has been modified to allow for a Python-style zip() as well as Ruby-style. The difference is explained in the comments:
/*
* This is a Python/Ruby style zip()
*
* zip(array $a1, array $a2, ... array $an, [bool $python=true])
*
* The last argument is an optional bool that determines the how the function
* handles when the array arguments are different in length
*
* By default, it does it the Python way, that is, the returned array will
* be truncated to the length of the shortest argument
*
* If set to FALSE, it does it the Ruby way, and NULL values are used to
* fill the undefined entries
*
*/
function zip() {
$args = func_get_args();
$ruby = array_pop($args);
if (is_array($ruby))
$args[] = $ruby;
$counts = array_map('count', $args);
$count = ($ruby) ? min($counts) : max($counts);
$zipped = array();
for ($i = 0; $i < $count; $i++) {
for ($j = 0; $j < count($args); $j++) {
$val = (isset($args[$j][$i])) ? $args[$j][$i] : null;
$zipped[$i][$j] = $val;
}
}
return $zipped;
}
Example:
$pythonzip = zip(array(1,2,3), array(4,5), array(6,7,8));
$rubyzip = zip(array(1,2,3), array(4,5), array(6,7,8), false);
echo '<pre>';
print_r($pythonzip);
print_r($rubyzip);
echo '<pre>';
// create
$a = array("a", "c", "e", "g", "h", "i");
$b = array("b", "d", "f");
$zip_array = array();
// get length of the longest array
$count = count(max($a, $b));
// zip arrays
for($n=0;$n<$count;$n++){
if (array_key_exists($n,$a)){
$zip_array[] = $a[$n];
}
if (array_key_exists($n,$b)){
$zip_array[] = $b[$n];
}
}
// test result
echo '<pre>'; print_r($zip_array); echo '<pre>';
function zip() {
$zip = [];
$arrays = func_get_args();
if ($arrays) {
$count = min(array_map('count', $arrays));
for ($i = 0; $i < $count; $i++) {
foreach ($arrays as $array) {
$zip[$i][] = $array[$i];
}
}
}
return $zip;
}
This works like in Python
function zip(...$arrays) {
return array_filter(
array_map(null, ...(count($arrays) > 1 ? $arrays : array_merge($arrays, [[]]))),
fn($z) => count($z) === count(array_filter($z)) || count($arrays) === 1
);
}
/**
* Takes an arbitrary number of arrays and "zips" them together into a single
* array, taking one value from each array and putting them into a sub-array,
* before moving onto the next.
*
* If arrays are uneven lengths, will stop at the length of the shortest array.
*/
function array_zip(...$arrays) {
$result = [];
$args = array_map('array_values',$arrays);
$min = min(array_map('count',$args));
for($i=0; $i<$min; ++$i) {
$result[$i] = [];
foreach($args as $j=>$arr) {
$result[$i][$j] = $arr[$i];
}
}
return $result;
}
Usage:
print_r(array_zip(['a','b','c'],[1,2,3],['x','y']));
Output:
Array
(
[0] => Array
(
[0] => a
[1] => 1
[2] => x
)
[1] => Array
(
[0] => b
[1] => 2
[2] => y
)
)
To overcome the issues with passing a single array to map_array, you can pass this function...unfortunately you can't pass "array" as it's not a real function but a builtin thingy.
function make_array() { return func_get_args(); }
Dedicated to those that feel like it should be related to array_combine:
function array_zip($a, $b)
{
$b = array_combine(
$a,
$b
);
$a = array_combine(
$a,
$a
);
return array_values(array_merge_recursive($a,$b));
}
you can see array_map method:
$arr1 = ['get', 'method'];
$arr2 = ['post'];
$ret = array_map(null, $arr1, $arr2);
output:
[['get', 'method'], ['post', null]]
php function.array-map
What is the most efficient way to check if an array is a flat array
of primitive values or if it is a multidimensional array?
Is there any way to do this without actually looping through an
array and running is_array() on each of its elements?
Use count() twice; one time in default mode and one time in recursive mode. If the values match, the array is not multidimensional, as a multidimensional array would have a higher recursive count.
if (count($array) == count($array, COUNT_RECURSIVE))
{
echo 'array is not multidimensional';
}
else
{
echo 'array is multidimensional';
}
This option second value mode was added in PHP 4.2.0. From the PHP Docs:
If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count() will recursively count the array. This is particularly useful for counting all the elements of a multidimensional array. count() does not detect infinite recursion.
However this method does not detect array(array()).
The short answer is no you can't do it without at least looping implicitly if the 'second dimension' could be anywhere. If it has to be in the first item, you'd just do
is_array($arr[0]);
But, the most efficient general way I could find is to use a foreach loop on the array, shortcircuiting whenever a hit is found (at least the implicit loop is better than the straight for()):
$ more multi.php
<?php
$a = array(1 => 'a',2 => 'b',3 => array(1,2,3));
$b = array(1 => 'a',2 => 'b');
$c = array(1 => 'a',2 => 'b','foo' => array(1,array(2)));
function is_multi($a) {
$rv = array_filter($a,'is_array');
if(count($rv)>0) return true;
return false;
}
function is_multi2($a) {
foreach ($a as $v) {
if (is_array($v)) return true;
}
return false;
}
function is_multi3($a) {
$c = count($a);
for ($i=0;$i<$c;$i++) {
if (is_array($a[$i])) return true;
}
return false;
}
$iters = 500000;
$time = microtime(true);
for ($i = 0; $i < $iters; $i++) {
is_multi($a);
is_multi($b);
is_multi($c);
}
$end = microtime(true);
echo "is_multi took ".($end-$time)." seconds in $iters times\n";
$time = microtime(true);
for ($i = 0; $i < $iters; $i++) {
is_multi2($a);
is_multi2($b);
is_multi2($c);
}
$end = microtime(true);
echo "is_multi2 took ".($end-$time)." seconds in $iters times\n";
$time = microtime(true);
for ($i = 0; $i < $iters; $i++) {
is_multi3($a);
is_multi3($b);
is_multi3($c);
}
$end = microtime(true);
echo "is_multi3 took ".($end-$time)." seconds in $iters times\n";
?>
$ php multi.php
is_multi took 7.53565130424 seconds in 500000 times
is_multi2 took 4.56964588165 seconds in 500000 times
is_multi3 took 9.01706600189 seconds in 500000 times
Implicit looping, but we can't shortcircuit as soon as a match is found...
$ more multi.php
<?php
$a = array(1 => 'a',2 => 'b',3 => array(1,2,3));
$b = array(1 => 'a',2 => 'b');
function is_multi($a) {
$rv = array_filter($a,'is_array');
if(count($rv)>0) return true;
return false;
}
var_dump(is_multi($a));
var_dump(is_multi($b));
?>
$ php multi.php
bool(true)
bool(false)
For PHP 4.2.0 or newer:
function is_multi($array) {
return (count($array) != count($array, 1));
}
I think this is the most straight forward way and it's state-of-the-art:
function is_multidimensional(array $array) {
return count($array) !== count($array, COUNT_RECURSIVE);
}
After PHP 7 you could simply do:
public function is_multi(array $array):bool
{
return is_array($array[array_key_first($array)]);
}
You could look check is_array() on the first element, under the assumption that if the first element of an array is an array, then the rest of them are too.
I think you will find that this function is the simplest, most efficient, and fastest way.
function isMultiArray($a){
foreach($a as $v) if(is_array($v)) return TRUE;
return FALSE;
}
You can test it like this:
$a = array(1 => 'a',2 => 'b',3 => array(1,2,3));
$b = array(1 => 'a',2 => 'b');
echo isMultiArray($a) ? 'is multi':'is not multi';
echo '<br />';
echo isMultiArray($b) ? 'is multi':'is not multi';
Don't use COUNT_RECURSIVE
click this site for know why
use rsort and then use isset
function is_multi_array( $arr ) {
rsort( $arr );
return isset( $arr[0] ) && is_array( $arr[0] );
}
//Usage
var_dump( is_multi_array( $some_array ) );
Even this works
is_array(current($array));
If false its a single dimension array if true its a multi dimension array.
current will give you the first element of your array and check if the first element is an array or not by is_array function.
You can also do a simple check like this:
$array = array('yo'=>'dream', 'mydear'=> array('anotherYo'=>'dream'));
$array1 = array('yo'=>'dream', 'mydear'=> 'not_array');
function is_multi_dimensional($array){
$flag = 0;
while(list($k,$value)=each($array)){
if(is_array($value))
$flag = 1;
}
return $flag;
}
echo is_multi_dimensional($array); // returns 1
echo is_multi_dimensional($array1); // returns 0
I think this one is classy (props to another user I don't know his username):
static public function isMulti($array)
{
$result = array_unique(array_map("gettype",$array));
return count($result) == 1 && array_shift($result) == "array";
}
In my case. I stuck in vary strange condition.
1st case = array("data"=> "name");
2nd case = array("data"=> array("name"=>"username","fname"=>"fname"));
But if data has array instead of value then sizeof() or count() function not work for this condition. Then i create custom function to check.
If first index of array have value then it return "only value"
But if index have array instead of value then it return "has array"
I use this way
function is_multi($a) {
foreach ($a as $v) {
if (is_array($v))
{
return "has array";
break;
}
break;
}
return 'only value';
}
Special thanks to Vinko Vrsalovic
Its as simple as
$isMulti = !empty(array_filter($array, function($e) {
return is_array($e);
}));
This function will return int number of array dimensions (stolen from here).
function countdim($array)
{
if (is_array(reset($array)))
$return = countdim(reset($array)) + 1;
else
$return = 1;
return $return;
}
Try as follows
if (count($arrayList) != count($arrayList, COUNT_RECURSIVE))
{
echo 'arrayList is multidimensional';
}else{
echo 'arrayList is no multidimensional';
}
$is_multi_array = array_reduce(array_keys($arr), function ($carry, $key) use ($arr) { return $carry && is_array($arr[$key]); }, true);
Here is a nice one liner. It iterates over every key to check if the value at that key is an array. This will ensure true