5
\$\begingroup\$

Let's say I've got this string:

$str = '1-2-3';

and I explode this like so:

$strSplit = explode('-', $str);

what would be the best way to check if I've got the correct number of elements and whether they are numeric (because the input can vary)?

Straightforward, I would say something like this:

if (isset($strSplit[0]) && is_numeric($strSplit[0]) && isset($strSplit[1]) && is_numeric($strSplit[1]) && isset($strSplit[2]) && is_numeric($strSplit[2]) {
    // some code
}

But you could also do:

if (count($strSplit) === 3 && is_numeric($strSplit[0]) && is_numeric($strSplit[1]) && is_numeric($strSplit[2])) {
    // some code
}

You can use some array functions to check for the is_numeric part, but that seems fussy, less readable and is probably slower. I could be wrong, though...

\$\endgroup\$
6
\$\begingroup\$

There are several options.

My favorite (based on readablity and simplicity) is using array_filter to remove everything that is not a number from the array and then comparing with the original to see if anything changed, then using count to make sure you have the correct number of elements:

$str = '1-foo-3';
$strSplit = explode('-', $str);

if(count($strSplit) == 3 && $strSplit == array_filter($strSplit, 'is_numeric')){
    // array is numeric // run code
} else {
    // array is non-numeric
}

Edit: did some speed testing and tried a few more options and validations.

I speedtested with the following inputs:

A: $strSplit = explode('-', '1-2-3');//3 elements, all numeric //evaluates true
B: $strSplit = explode('-', '1-foo-3-4');//4 elements, not all numeric //evaluates false

The results (after 1 million cycles) were as follows :

Static isset and is_numeric :

if (isset($strSplit[0]) && is_numeric($strSplit[0]) && isset($strSplit[1]) && is_numeric($strSplit[1]) && isset($strSplit[2]) && is_numeric($strSplit[2]) && !isset($strSplit[3])) {
    // some code
    // A : speed 0.68 mircoseconds
    // B : speed 0.48 mircoseconds
}

Looped isset and is_numeric :

for($i=0; $i<3;$i++) if(!isset($strSplit[$i]) || !is_numeric($strSplit[$i])) $nonNumeric = true;

if(!isset($strSplit[($i+1)]) && !isset($nonNumeric)){
    // some code
    // A : speed 0.96 mircoseconds
    // B : speed 1.00 mircoseconds
}

count and array_filter :

if(count($strSplit) == 3 && $strSplit == array_filter($strSplit, 'is_numeric')){
    // some code
    // A : speed 2.00 mircoseconds
    // B : speed 0.16 mircoseconds
}
\$\endgroup\$
  • 1
    \$\begingroup\$ This is very clever and neat. Also nice is the fact that you can use it for any size string/array, which is what I don't like about the 'regular' approach, where you have to keep adding isset and is_numeric for every extra element. Only downside is that array functions are slower. Using isset and is_numeric seems to be the fastest (I did some speed testing): 0.81. With count instead of isset that goes up to 0.88, while your solution shows 1.50. However, this is for half a million iterations, so not a noticeable difference. \$\endgroup\$ – kasimir May 21 '14 at 7:19
  • \$\begingroup\$ @kaisimir try speed testing against the string '1-foo-3-4'. The results are suprising ; ) \$\endgroup\$ – Ben A. Noone May 21 '14 at 16:10
  • \$\begingroup\$ Nevermind, I edited my answer to post the results. \$\endgroup\$ – Ben A. Noone May 21 '14 at 16:24
  • 1
    \$\begingroup\$ I couldn't find any speed differences myself testing against a 'wrong' string. But taking all the results together I'd say count (vs. isset) is the way to go to get the right amount of elements. The testing for numeric is more difficult. I don't like the solutions using for loops and using an extra variable, it's poorly readable. As for the static is_numeric and array_filter options: if I'd have two/three elements the is_numeric is most straightforward and the fastest. Anything more than that would mean (a lot of) extra code, so I would go for array_filter in that case. \$\endgroup\$ – kasimir May 22 '14 at 14:33
2
\$\begingroup\$

Using array functions could be the best choice. If you wanted to work on only the input that is numeric, you could use the array_filter function to get only those that are numeric. Otherwise, if you only wanted to work on the list provided all the input is numeric, a custom function would probably be the best. I have provided both styles below:

Version 1 - All Numeric

<?php
    // Checks to see if all input is numeric. Returns false
    // on the first occurance of non-numeric data.
    function all_numeric($list)
    {
        foreach($list as $val)
        {
           if(!is_numeric($val))
           {
               return false;
           }
        }

        return true;
    }

    $expected_length = 4;
    $split = explode('-', '1-2-3');

    // Only proceed if we have as much data as we expected and if all
    // the data is numeric.
    if(count($split) == $expected_length and all_numeric($split))
    {
        . . .
    }
?>

Version 2 - Some Numeric

<?php

    $expected_length = 4;
    $split = explode('-', '1-2-3');

    // Check to see if we have as much data as we expected.
    // No need to do anything else if not.
    if(count($split) == $expected_length)
    {
        // Filter the array to get only the values that are numeric.
        $split = array_filter($split, 'is_numeric');
        . . .
    }
?>
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.