开发者

Determine whether an array is associative (hash) or not [duplicate]

This question already has answers here: How to check if PHP array is associative or sequential? (60 answers) 开发者_运维百科 Closed last year.

I'd like to be able to pass an array to a function and have the function behave differently depending on whether it's a "list" style array or a "hash" style array. E.g.:

myfunc(array("One", "Two", "Three")); // works
myfunc(array(1=>"One", 2=>"Two", 3=>"Three")); also works, but understands it's a hash

Might output something like:

One, Two, Three
1=One, 2=Two, 3=Three

ie: the function does something differently when it "detects" it's being passed a hash rather than an array. Can you tell I'm coming from a Perl background where %hashes are different references from @arrays?

I believe my example is significant because we can't just test to see whether the key is numeric, because you could very well be using numeric keys in your hash.

I'm specifically looking to avoid having to use the messier construct of myfunc(array(array(1=>"One"), array(2=>"Two"), array(3=>"Three")))


Pulled right out of the kohana framework.

public static function is_assoc(array $array)
{
    // Keys of the array
    $keys = array_keys($array);

    // If the array keys of the keys match the keys, then the array must
    // not be associative (e.g. the keys array looked like {0:0, 1:1...}).
    return array_keys($keys) !== $keys;
}


This benchmark gives 3 methods.

Here's a summary, sorted from fastest to slowest. For more informations, read the complete benchmark here.

1. Using array_values()

function($array) {
    return (array_values($array) !== $array);
}

2. Using array_keys()

function($array){
    $array = array_keys($array); return ($array !== array_keys($array));
}

3. Using array_filter()

function($array){
    return count(array_filter(array_keys($array), 'is_string')) > 0;
}


PHP treats all arrays as hashes, technically, so there is not an exact way to do this. Your best bet would be the following I believe:

if (array_keys($array) === range(0, count($array) - 1)) {
   //it is a hash
}


No, PHP does not differentiate arrays where the keys are numeric strings from the arrays where the keys are integers in cases like the following:

$a = array("0"=>'a', "1"=>'b', "2"=>'c');
$b = array(0=>'a', 1=>'b', 2=>'c');

var_dump(array_keys($a), array_keys($b));

It outputs:

array(3) {
    [0]=> int(0) [1]=> int(1) [2]=> int(2)
}
array(3) {
    [0]=> int(0) [1]=> int(1) [2]=> int(2)
}

(above formatted for readability)


My solution is to get keys of an array like below and check that if the key is not integer:

private function is_hash($array) {
    foreach($array as $key => $value) {
        return ! is_int($key);
    }
    return false;
}

It is wrong to get array_keys of a hash array like below:

array_keys(array(
       "abc" => "gfb",
       "bdc" => "dbc"
       )
);

will output:

array(
       0 => "abc",
       1 => "bdc"
)

So, it is not a good idea to compare it with a range of numbers as mentioned in top rated answer. It will always say that it is a hash array if you try to compare keys with a range.


Being a little frustrated, trying to write a function to address all combinations, an idea clicked in my mind: parse json_encode result.

When a json string contains a curly brace, then it must contain an object!

Of course, after reading the solutions here, mine is a bit funny... Anyway, I want to share it with the community, just to present an attempt to solve the problem from another prospective (more "visual").


function isAssociative(array $arr): bool
    {
        // consider empty, and [0, 1, 2, ...] sequential
        if(empty($arr) || array_is_list($arr)) {
            return false;
        }

        // first scenario:
        // [  1  => [*any*] ]
        // [ 'a' => [*any*] ]
        foreach ($arr as $key => $value) {
            if(is_array($value)) {
                return true;
            }
        }

         // second scenario: read the json string
        $jsonNest = json_encode($arr, JSON_THROW_ON_ERROR);

        return str_contains($jsonNest, '{'); // {} assoc, [] sequential
    }

NOTES

php@8.1 is required, check out the gist on github containing the unit test of this method + Polyfills (php>=7.3).

I've tested also Hussard's posted solutions, A & B are passing all tests, C fails to recognize: {"1":0,"2":1}.

BENCHMARKS

Here json parsing is ~200 ms behind B, but still 1.7 seconds faster than solution C!

What do you think about this version? Improvements are welcome!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜