Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enforce array length equality

Tags:

arrays

php

TL;DR

Given an array of arrays, I need to make sure that every array has the same length of the first.

The original problem

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

How is the current implementation

I implemented it using array_map:

<?php
$items = array_map(function (...$items) {
    return $items;
}, ...$values);

What I need to achieve

When an array is shorter than the first I need to throw a LengthException.

The ideal solution

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);

Why the ideal solution doesn't work

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.

My first and cumbersome solution

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.

like image 213
Álvaro Guimarães Avatar asked Aug 14 '26 03:08

Álvaro Guimarães


1 Answers

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.

like image 142
dbrumann Avatar answered Aug 16 '26 18:08

dbrumann



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!