Given an array of arrays, I need to make sure that every array has the same length of the first.
I have a function that should transpose a matrix lines to columns.
Original Result
1,2,3 1,4,7
4,5,6 2,5,8
7,8,9 3,6,9
I implemented it using array_map
:
<?php
$items = array_map(function (...$items) {
return $items;
}, ...$values);
When an array is shorter than the first I need to throw a LengthException
.
The ideal solution would be storing the length of the first element outside the array_map and just compare with the length of the current item being iterated by array_map
, like this:
<?php
$expectedLength = count(reset($values));
$items = array_map(function (...$items) {
$length = count($items);
if ($length !== $expectedLength) {
throw new LengthException("Element size differs ({$length} should be {$expectedLength})");
}
return $items;
}, ...$values);
From the PHP manual:
Usually when using two or more arrays, they should be of equal length because the callback function is applied in parallel to the corresponding elements. If the arrays are of unequal length, shorter ones will be extended with empty elements to match the length of the longest.
Sadly, the shorter arrays will be filled with empty elements.
Since the original array could have empty elements, I'm left with no other way to test if the array was originally shorter than the first.
I have to test it before array_map
and I'm not convinced that this is a good solution:
<?php
$expectedLength = count(reset($values));
$diffLength = count(array_intersect_key(...$values));
if ($diffLength !== $expectedLength) {
throw new LengthException("Element size differs ({$diffLength} should be {$expectedLength})");
}
$items = array_map(function (...$items) {
return $items;
}, ...$values);
Would you be kind enough to help me find a better way? I would like to do it inside the iteration, but if you find a better way to find if we have an element with a different length (I don't like to be using array_intersect_key
) before the iteration, that's OK too.
You can keep your ideal solution with a small adjustment:
array_map(function (...$items) {
$nonNullItems = array_filter(
$items,
function ($item) { return $item !== null }
);
$length = count($nonNullItems);
if ($length !== $expectedLength) {
throw new LengthException(
"Element size differs ({$length} should be {$expectedLength})"
);
}
return $items;
}
Note this addition:
array_filter($items, function ($item) { return $item !== null});
It will filter out all elements in items that are null (but not 0 or .0).
edit: I'm not sure but array_filter($items)
might work as well, but I'm not sure if without a callback empty or null items are dismissed so the above is making sure only null is dismissed.
Codes: (Demo)
$arrays=[
[['a','b','c'],['d','e','f'],['g','h','i']], // test array #1: no missing elements
[['a','b','c'],['d','e','f'],['g','h']], // test array #2: missing element i
[['a','b','c'],['d','e'],['g','h','i']], // test array #3: missing element f
[['a','b'],['d','e','f'],['g','h','i']], // test array #4: missing element c
[['a','b','c'],['d','e','f'],['g','h',NULL]], // test array #5: NULL on i
];
// find non-specific imbalance using COUNT_RECURSIVE
foreach($arrays as $i=>$a){ // running 5 separate tests
echo "Test #$i:
";
if(($originalsize=sizeof($a,1))===($rotatedsize=sizeof(array_map(function(){return func_get_args();},...$a),1))){
echo "@$i originalsize ($originalsize) = rotatedsize ($rotatedsize)
";
}else{
echo "@$i originalsize ($originalsize) DOES NOT EQUAL rotatedsize ($rotatedsize)
";
}
}
echo "
---
";
// Find specific subarray imbalances using COUNT
foreach($arrays as $i1=>$a){ // running 5 separate tests
echo "Test #$i1:
";
$firstsize=sizeof($a[0]);
foreach(array_slice($a,1,null,true) as $i2=>$others){
if($firstsize==($othersize=sizeof($others))){
echo "The first size ($firstsize) = subarray $i2 size ($othersize)
";
}else{
echo "The first size ($firstsize) DOES NOT EQUAL subarray $i2 size ($othersize)
";
}
}
echo "
";
}
Output:
Test #0:
@0 originalsize (12) = rotatedsize (12)
Test #1:
@1 originalsize (11) DOES NOT EQUAL rotatedsize (12)
Test #2:
@2 originalsize (11) DOES NOT EQUAL rotatedsize (12)
Test #3:
@3 originalsize (11) DOES NOT EQUAL rotatedsize (12)
Test #4:
@4 originalsize (12) = rotatedsize (12)
---
Test #0:
The first size (3) = subarray 1 size (3)
The first size (3) = subarray 2 size (3)
Test #1:
The first size (3) = subarray 1 size (3)
The first size (3) DOES NOT EQUAL subarray 2 size (2)
Test #2:
The first size (3) DOES NOT EQUAL subarray 1 size (2)
The first size (3) = subarray 2 size (3)
Test #3:
The first size (2) DOES NOT EQUAL subarray 1 size (3)
The first size (2) DOES NOT EQUAL subarray 2 size (3)
Test #4:
The first size (3) = subarray 1 size (3)
The first size (3) = subarray 2 size (3)