Considerations
In accordance with official documentation, one array may only have keys of the whole type or strings. The value can be of any type.
The key can be an integer or a string. The value can be of any type.
Some coercion that may occur:
- Strings containing valid integers, will be converted to the whole type. For example, the key
"8"
will, in fact, be stored as 8
. Meanwhile, "08"
will not be converted as it is not a valid decimal integer.
Therefore, the array ["8" => "foo"]
should be considered as array of numerical and non-associative indices.
- Floats are also converted to integers; this means that the fractional part will be removed. For example, the key
8.7
will actually be stored as 8
.
Therefore, the array [8.7 => "foo"]
should be considered as array of numerical indexes also.
Floats will also be converted to integers including negative values. The key -5.6
will actually be stored as -5
.
- Boolean are converted to integers, also; for example, the key
true
, will actually be stored as 1
and the key false
as 0
.
Therefore, the array [true => "foo", false => "bar]
should be considered as array of numerical indexes also.
- Null will be converted to a string empty; for example, the key
null
will actually be stored as ""
.
Therefore, the array [null => "foo"]
should be considered as array associative.
Other considerations:
Arrays mixed, such as ["foo", "key" => "bar"]
shall be regarded as associations and numerical indices.
A array of numerical indices does not cease to be so when their indices are not sequential; therefore array [1 => "foo", 0 => "bar"]
should also be considered as numerical indexes.
Similarly, a array with numeric indices that have one of their values removed (or that the sequence of the indices is not natural) will also remain numeric indices; therefore the array [0 => "foo", 1 => "bar", 6 => "baz"]
shall be considered to be of numerical indices.
Contexts to be tested
Heed! For the remainder of the response, a array sequential said is, in fact, a array of numerical indices, but not necessarily of sequential indices.
That said, I have defined some tests that functions should be submitted:
/**
* Lista de testes que a função deve ser submetida.
*
* Cada item da lista deve ser um array com três índices.
* O primeiro, "array", com o array a ser testado pela função.
* O segundo, "is_sequential", um valor booleano esperado como retorno da função is_sequential.
* O terceiro, "is_associative", um valor booleano esperado como retorno da função is_associative.
*/
$tests = array();
// Teste 1: Array com índices numéricos sequenciais
$tests[] = [
"array" => ["a", "b", "c", "d", "e"],
"is_sequential" => true,
"is_associative" => false
];
// Teste 2: Array associativo
$tests[] = [
"array" => ["name" => "foo", "lastname" => "bar"],
"is_sequential" => false,
"is_associative" => true
];
// Teste 3: Array com chave do tipo string contendo inteiro válido
$tests[] = [
"array" => ["0" => "foo", "1" => "bar"],
"is_sequential" => true,
"is_associative" => false
];
// Teste 4: Array com índices do tipo float
$tests[] = [
"array" => [0.5 => "foo", -3.5 => "bar"],
"is_sequential" => true,
"is_associative" => false
];
// Teste 5: Array com índices do tipo booleanos
$tests[] = [
"array" => [true => "foo", false => "bar"],
"is_sequential" => true,
"is_associative" => false
];
// Teste 6: Array com índice nulo
$tests[] = [
"array" => [null => "foo"],
"is_sequential" => false,
"is_associative" => true
];
// Teste 7: Array misto
$tests[] = [
"array" => ["foo", "baz" => "bar"],
"is_sequential" => false,
"is_associative" => false
];
// Teste 8: Array de índices numéricos desordenados
$tests[] = [
"array" => [1 => "foo", 0 => "bar"],
"is_sequential" => true,
"is_associative" => false
];
// Teste 9: Array de índices numéricos ordenados não sequenciais
$tests[] = [
"array" => [0 => "foo", 1 => "bar", 6 => "baz"],
"is_sequential" => true,
"is_associative" => false
];
Solution
The simplest solution I can see is to check the key type of the array, then:
/**
* Função que testa se o array é sequencial.
*
* @param array Array a ser testado
* @return bool True se $array for sequencial, False caso contrário
*/
function is_sequential (array $array) {
return array_filter($array, "is_int", ARRAY_FILTER_USE_KEY) == true;
}
/**
* Função que testa se o array é associativo.
*
* @param array Array a ser testado
* @return bool True se $array for associativo, False caso contrário
*/
function is_associative (array $array) {
return array_filter($array, "is_string", ARRAY_FILTER_USE_KEY) == true;
}
The above solutions use PHP’s native coercion in considering a array not empty as true
and a array empty as false
. Therefore, if any key/value of the array pass through the defined filter, the array resulting will be non-empty and the function will return true
. If no key/value pair passes through the filter, the array result will be empty and the function return will be false
.
Testing
The verification of the tests is done as follows:
try {
foreach ($tests as $i => $test) {
if ($test["is_sequential"] !== is_sequential($test["array"])) {
throw new Exception(sprintf("is_sequential: Algo errado não está certo! Teste %d falhou.", $i+1));
}
if ($test["is_associative"] !== is_associative($test["array"])) {
throw new Exception(sprintf("is_associative: Algo errado não está certo! Teste %d falhou.", $i+1));
}
}
echo "Parabéns! Sua função passou em todos os testes.", PHP_EOL;
} catch (Exception $e) {
echo $e->getMessage(), PHP_EOL;
}
Upshot
The result for the defined functions can be found in the Repl.it, or in the Ideone.
Inconsistencies
The solution presented above works according to the specifications of PHP itself; that is, if a array has at least one value that can be accessed via the numeric key, the array must be considered array of numerical indices; if the array have at least one value that can be accessed via type key string, the array must be considered array associative. Therefore, in the solution presented, a array mixed must return true to both functions. The inconsistency (semantics) that this solution generates is present when considering the following code snippet:
if (is_sequential($array)) {
for ($i = 0; $i < count($array); $i++) {
echo $array[$i], PHP_EOL;
}
}
That is, if the array is numerically indexed, traverse all elements and display them on the screen. If the array is mixed, one of the elements will not be accessible in this way and at some point the index $i
will not exist, resulting in error Undefined offset
.
One way to circumvent this inconsistency is to add a new parameter to the functions named $strict
, boolean type, that when true, the function is_sequential
true return only when the array is strictly numerical and the function is_associative
return true only when the array is strictly associative. In this way, the solution would be:
/**
* Função que testa se o array é sequencial.
*
* @param array Array a ser testado
* @param bool Define se a verificação deve ser de modo rigoroso
* @return bool True se $array for sequencial, False caso contrário
*/
function is_sequential (array $array, bool $strict = false) {
return array_filter($array, "is_int", ARRAY_FILTER_USE_KEY) == ($strict ? $array : true);
}
/**
* Função que testa se o array é associativo.
*
* @param array Array a ser testado
* @param bool Define se a verificação deve ser de modo rigoroso
* @return bool True se $array for associativo, False caso contrário
*/
function is_associative (array $array, bool $strict = false) {
return array_filter($array, "is_string", ARRAY_FILTER_USE_KEY) == ($strict ? $array : true);
}
In this way, the above-mentioned example would be:
// Array puramente de índices numéricos
$array = ["foo", "baz", "bar"];
// Passará pela condição e será exibido
if (is_sequential($array, true)) {
for ($i = 0; $i < count($array); $i++) {
echo $array[$i], PHP_EOL;
}
}
// Array misto
$array = ["foo", "baz" => "bar"];
// Não passará pela condição e não será exibido
if (is_sequential($array, true)) {
for ($i = 0; $i < count($array); $i++) {
echo $array[$i], PHP_EOL;
}
}
This example can be seen in Repl.it or in the Ideone.
Other solutions put forward
Note: The following checks of the other solutions presented here are for comparison only when considering all the situations foreseen by the documentation, through the above coercion. It is expected that they will fail some tests, as they do not take into account all the situations mentioned. This comparison should not be seen as a form of demerit on my part, demeaning the respective authors. I even believe faithfully that any kind of content here is taken advantage of in some way.
The solution presented here, by Brunorb, would fail tests 4, 5, 7, 8 and 9. See tests here.
The solution presented here, by William Nascimento, would fail tests 4, 5, 7, 8 and 9. See tests here.
The solution presented here, by Wallace Maxters, would fail tests 4, 5, 7, 8 and 9. See tests here.
The solution presented here (adapted), by Andrei Coelho, would fail tests 4, 7 and 9. See tests here.
At the end you want the return for example, array
x
is numerical, orx
is associative orx
is mixed. I see zebras hahaha :D– rray
@rray I ended up answering my question too. The
array
in PHP whether or not it’s a mess!– Wallace Maxters
Probably just looking at the fountain. Pro PHP, apparently always associative: http://ideone.com/iEDhkz - I doubt very much that there is a flag indicating the difference internally (and I’m not very keen to look at the source for this).
– Bacco
Actually, I think I’m more into Splfixedarray
– Wallace Maxters