PHP: How do you check whether an array is a list or associative?
PHP has only one array type, which serves as both a list and a dictionary. Sometimes you need to know which of the two you are holding — and by the time you get to JSON it definitely matters.
From PHP 8.1 onwards
Since PHP 8.1 there is a built-in function for this:
array_is_list(). It
returns true when the keys start at 0, are integers and increase without
gaps:
['a','b','c'] => true
[0=>'a', 1=>'b'] => true
[1=>'a', 2=>'b'] => false (does not start at 0)
[0=>'a', 2=>'b'] => false (gap)
['x'=>1] => false
[] => true (the empty array counts as a list)
The last case is deliberate: an empty array can be regarded as an empty list without contradiction.
Before PHP 8.1
If you are on an older version, you will find this rule of thumb everywhere:
<?php
function isList(array $a): bool {
return array_keys($a) === range(0, count($a) - 1);
}
?>
It works — except in one case. Let us compare it with the built-in function:
old formula array_is_list
['a','b'] true true
[] false true <- differs
[1=>'a'] false false
['0'=>'a'] true true
For the empty array the old formula returns false, so it considers it
associative. The reason lies in range():
range(0, -1) => [0, -1]
If the start value is greater than the end value, range() counts backwards. So
for an empty array [] is compared with [0, -1], and those are, as you would
expect, not the same.
If you still have this formula somewhere in your code, it is worth searching for it deliberately. If you have to stay on PHP 8.0 or older, an extra check helps:
<?php
function isList(array $a): bool {
if ($a === []) {
return true;
}
return array_keys($a) === range(0, count($a) - 1);
}
?>
Numeric keys as strings
A detail that often causes confusion: PHP automatically converts numeric string keys into integers.
['0'=>'x','1'=>'y'] keys : [0,1]
type of the first key : int
So ['0' => 'a'] does not merely behave like a list, it is one. This
conversion always happens, not just during comparison.
Why this matters at all
The distinction feels academic until you have overlooked it once at an
interface. json_encode() produces different formats:
json_encode(['a','b','c']) => ["a","b","c"]
An array. Now let us remove an element:
<?php
$list = ['a', 'b', 'c'];
unset($list[1]);
echo json_encode($list);
?>
{"0":"a","2":"c"}
The array has become an object. And it has to, because a JSON array has no keys with which the gap could be expressed.
For the client of your interface, though, that is a change of format. Anyone expecting an array and suddenly receiving an object breaks — and only sometimes, namely exactly when an element was removed. Bugs like that take a long time to find.
The fix is an array_values() before the data leaves the building:
json_encode(array_values($list)) => ["a","c"]
Summary
- From PHP 8.1:
array_is_list(). - The old
range()formula answers the empty array wrongly. - Numeric string keys automatically become integers.
- Call
array_values()beforejson_encode()if the result should be a JSON array.
About Netcup (advertisement)
The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).