O PHP trata todas as matrizes como associativas, portanto, não há funções incorporadas. Alguém pode recomendar uma maneira bastante eficiente de verificar se uma matriz contém apenas chaves numéricas?
Basicamente, eu quero ser capaz de diferenciar entre isso:
$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');
e isto:
$assocArray = array('fruit1' => 'apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot');
if (isset($array[0]))
, o que é simples e rápido. Obviamente, você deve primeiro garantir que a matriz não esteja vazia e ter algum conhecimento sobre o conteúdo possível da matriz, para que o método não possa falhar (como numérico / associativo ou não sequencial).Respostas:
Você fez duas perguntas que não são totalmente equivalentes:
Considere qual desses comportamentos você realmente precisa. (Pode ser que isso funcione para seus propósitos.)
A primeira pergunta (simplesmente verificando se todas as teclas são numéricas) é respondida bem pelo capitão kurO .
Para a segunda pergunta (verificando se a matriz é zero-indexada e seqüencial), você pode usar a seguinte função:
fonte
isSequential()
faria mais sentido do queisAssoc()
. Nessa função, a matriz vazia deve ser vista como seqüencial. A fórmula poderia serarray() === $arr || !isAssoc($arr)
.array_key_exists
vez de,isset
porque se o elemento zero for um valor nulo, o isset retornará falso incorretamente. Um valor nulo normalmente deve ser um valor legítimo nessa matriz.Para apenas verificar se a matriz possui chaves não inteiras (não se a matriz é indexada seqüencialmente ou indexada a zero):
Se houver pelo menos uma chave de cadeia,
$array
será considerada uma matriz associativa.fonte
$isIndexed = array_values($arr) === $arr;
? Para o qual pergunto: como você acha quearray_values()
funciona? Como você acha que===
aplicado a matrizes funciona? A resposta é obviamente que eles também iteram sobre a matriz.var_dump([1.2 => 'foo', 1.5 => 'bar']);
descobrirá que obtém a matriz[1 => 'bar']
. Não há como descobrir o tipo original de uma chave. Sim, tudo isso é horrível; As matrizes do PHP são, de longe, a pior parte da linguagem, e a maior parte dos danos é irreparável e deve-se à idéia de usar uma única construção para matrizes tradicionais e hashmaps tradicionais, sendo uma terrível desde o início.function isAssociative($arr) { foreach ($arr as $key => $value) { if (is_string($key)) return true; } return false; }
array(1 => 'a', 0 => 'b', 2 => 'c')
se tornaráfalse
(matriz seqüencial) enquanto deveria sertrue
(matriz associativa). toolsqa.com/data-structures/array-in-programming Não sei se a chave deve estar em ordem crescente? (0, 1, ...)Certamente esta é uma alternativa melhor.
fonte
===
perderemos tempo verificando se os valores são iguais, mesmo que apenas estejamos interessados nas chaves. Por esse motivo, prefiro a$k = array_keys( $arr ); return $k === array_keys( $k );
versão.Muitos comentadores nesta pergunta não entendem como as matrizes funcionam no PHP. Na documentação da matriz :
Em outras palavras, não existe uma chave de matriz "8" porque ela sempre será (silenciosamente) convertida no número inteiro 8. Portanto, tentar diferenciar números inteiros e seqüências numéricas é desnecessário.
Se você deseja a maneira mais eficiente de verificar uma matriz em busca de chaves não inteiras sem fazer uma cópia de parte da matriz (como array_keys () faz) ou de tudo isso (como foreach faz):
Isso funciona porque key () retorna NULL quando a posição atual da matriz é inválida e NULL nunca pode ser uma chave válida (se você tentar usar NULL como uma chave de matriz, ela será convertida silenciosamente em "").
fonte
0
acount($array)-1
, nesta ordem estrita. Uma verificação preliminar comis_array()
pode ajudar. Adicione uma variável crescente para verificar a sequência de teclas:for ($k = 0, reset($array) ; $k === key($array) ; next($array)) ++$k;
Isso estabelece o acordo.foreach
vez de iteração explícita é duas vezes mais rápido.function isAssocStr($array) { for (reset($array); is_int(key($array)); next($array)) { if (is_null(key($array))) return false; } return true; }
Conforme declarado pelo OP :
não é muito sensato (IMHO) escrever uma função que verifique se uma matriz é associativa . Então, a primeira coisa: o que é uma chave em uma matriz PHP ?
Isso significa que existem três casos possíveis:
Podemos verificar cada caso com as seguintes funções.
Caso 1: todas as teclas são numéricas / inteiras .
Nota : Esta função também retorna true para matrizes vazias.
Caso 2: todas as chaves são cadeias de caracteres .
Nota : Esta função também retorna true para matrizes vazias.
Caso 3. algumas chaves são seqüências de caracteres , outras são numéricas / números inteiros .
Nota : Esta função também retorna true para matrizes vazias.
Segue que:
(que é por definição, como em " o conjunto vazio é um subconjunto de qualquer conjunto A porque todos os seus elementos pertencem a A ").
Agora, para uma matriz ser uma matriz "genuína" à qual estamos acostumados, o que significa:
Podemos verificar com a seguinte função.
Caso 3a. As teclas são numéricas / inteiras , seqüenciais e baseadas em zero .
Nota : Esta função também retorna true para matrizes vazias.
Advertências / Armadilhas (ou, fatos ainda mais peculiares sobre chaves de array em PHP)
Chaves inteiras
As chaves para essas matrizes são números inteiros :
Teclas de cadeia
As chaves para essas matrizes são cadeias de caracteres :
Chaves inteiras que se parecem com seqüências de caracteres
Se você acha que a chave
array("13" => "b")
é uma string , está errado . Do documento aqui :Por exemplo, a chave para essas matrizes são números inteiros :
Mas a chave para essas matrizes são as strings :
Além do mais, de acordo com o documento ,
Portanto, a chave para esta matriz pode ou não ser um número inteiro - depende da sua plataforma.
Pior ainda, o PHP tende a ser buggy se o número inteiro estiver próximo do limite 2 31 = 2.147.483.648 (veja o bug 51430 , o bug 52899 ). Por exemplo, no meu ambiente local (PHP 5.3.8 no XAMPP 1.7.7 no Windows 7),
var_dump(array("2147483647" => "b"))
fornecemas nesta demonstração ao vivo no codepad (PHP 5.2.5), a mesma expressão fornece
Portanto, a chave é um número inteiro em um ambiente, mas uma sequência em outro, mesmo sendo
2147483647
um número inteiro de 32 bits com sinal válido .fonte
Velocidade:
Em termos de memória:
fonte
fonte
array('1'=>'asdf', '2'=>'too')
será considerado como matriz associativa enquanto não é verdade (as chaves são realmente string)true
se as teclas forem: zero, números inteiros (somente positivo), uma sequência vazia ou qualquer combinação das opções acima, como a sequência "09". Esta função não leva em consideração a ordem das chaves. Entãoarray(0=>'blah', 2=>'yep', 3=>'wahey')
,array(0=>'blah', 2=>'yep', 1=>'wahey')
earray('blah', 'yep', 'wahey')
são todos associativos de acordo com essa função, enquantoarray('a'=>'blah', 'b'=>'yep', 'c'=>'wahey')
não são.Na verdade, a maneira mais eficiente é assim:
Isso funciona porque compara as chaves (que para uma matriz seqüencial são sempre 0,1,2 etc) às chaves das chaves (que sempre serão 0,1,2 etc).
fonte
true
paraarray(1=>"a")
masfalse
paraarray("a"=>"a")
. Seria mais significativo se!=
for substituído por!==
.[0] == ['a']
em PHP (desde0 == 'a'
e, de fato0 == 'banana'
). O==
operador do PHP é insano.Eu usei ambos
array_keys($obj) !== range(0, count($obj) - 1)
earray_values($arr) !== $arr
(que são duplos um do outro, embora o segundo seja mais barato que o primeiro), mas ambos falham em matrizes muito grandes.Isto porque
array_keys
earray_values
são ambos operações muito caras (uma vez que eles constroem uma nova série de tamanho mais ou menos a do original).A função a seguir é mais robusta que os métodos fornecidos acima:
Observe também que, se você não deseja diferenciar matrizes esparsas de matrizes associativas, pode simplesmente retornar
'assoc'
dos doisif
blocos.Finalmente, embora isso possa parecer muito menos "elegante" do que muitas "soluções" nesta página, na prática é muito mais eficiente. Quase qualquer array associativo será detectado instantaneamente. Somente matrizes indexadas serão verificadas exaustivamente, e os métodos descritos acima não apenas verificam matrizes indexadas exaustivamente, elas as duplicam.
fonte
Eu acho que as duas funções a seguir são a melhor maneira de verificar 'se uma matriz é associativa ou numérica'. Como 'numérico' pode significar apenas teclas numéricas ou apenas teclas numéricas seqüenciais, duas funções são listadas abaixo que verificam uma das condições:
A primeira função verifica se cada chave é um valor inteiro. A segunda função verifica se cada chave é um valor inteiro e, além disso, verifica se todas as chaves são seqüenciais iniciando em $ base, cujo padrão é 0 e, portanto, pode ser omitido se você não precisar especificar outro valor base. key ($ my_array) retorna null se o ponteiro de leitura for movido além do final da matriz, que é o que termina o loop for e faz com que a instrução após o loop for retorne true se todas as chaves forem inteiras. Caso contrário, o loop será encerrado prematuramente porque uma chave é do tipo string e a instrução após o loop for retornará false. A última função, além disso, adiciona um a $ base após cada comparação, para poder verificar se a próxima chave tem o valor correto. A comparação estrita faz com que também verifique se a chave é do tipo inteiro. A parte $ base = (int) $ base na primeira seção do loop for pode ser deixada de fora quando $ base for omitida ou se você se certificar de que ela é chamada apenas usando um número inteiro. Mas como não posso ter certeza de todos, deixei-o. A declaração é executada apenas uma vez. Penso que estas são as soluções mais eficientes:
Lembre-se de que uma chave de matriz pode ser apenas um número inteiro ou uma sequência de caracteres, e uma sequência estritamente numérica como "1" (mas não "01") será convertida em um número inteiro. É isso que torna a verificação de uma chave inteira a única operação necessária, além de contar se você deseja que o array seja seqüencial. Naturalmente, se is_indexed_array retornar false, o array poderá ser visto como associativo. Eu digo 'visto', porque na verdade todos eles são.
fonte
Esta função pode lidar com:
a idéia é simples: se uma das chaves NÃO é um número inteiro, é um array associativo, caso contrário, é seqüencial.
fonte
Notei duas abordagens populares para essa pergunta: uma usando
array_values()
e outra usandokey()
. Para descobrir o que é mais rápido, escrevi um pequeno programa:A saída para o programa no PHP 5.2 no CentOS é a seguinte:
A saída no PHP 5.3 produziu resultados semelhantes. Obviamente, o uso
array_values()
é muito mais rápido.fonte
$arrays = Array( 'Array #1' => range(0, 50000), );
Uma maneira de abordar isso é pegar carona
json_encode
, que já possui seu próprio método interno de diferenciação entre uma matriz associativa e uma matriz indexada para gerar o JSON correto.Você pode fazer isso verificando se o primeiro caractere retornado após a codificação é um
{
(array associativo) ou um[
(array indexado).fonte
Já existem muitas respostas, mas aqui está o método em que o Laravel conta com sua classe Arr:
Fonte: https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php
fonte
array_keys($keys)
retornará uma matriz seqüencial de números (0 ... X) que possui o mesmo comprimento da matriz original. Por exemploarray_keys(["a", "b", "c"]) = [0, 1, 2];
array_keys([0, 1, 2]) = [0, 1, 2]
(é uma matriz seqüencial porque[0, 1, 2] !== [0, 1, 2]
). Outro exemplo:array_keys(["a" => 5, "b" => 7, "c" => 10]) = ["a", "b", "c"];
array_keys(["a", "b", "c"]) = [0, 1, 2]
(é uma matriz associativa porque["a", "b", "c"] !== [0, 1, 2]
). Espero que seja claro (difícil de explicar extensivamente em um comentário, pelo menos para mim)Rápido, conciso e com eficiência de memória. Sem comparações caras, chamadas de função ou cópia de matriz.
fonte
Usando a extensão PHP xarray
Você pode fazer isso muito rápido (cerca de 30 vezes mais rápido no PHP 5.6):
Ou:
fonte
Eu sei que é um pouco inútil adicionar uma resposta a essa fila enorme, mas aqui está uma solução O (n) legível que não requer duplicação de nenhum valor:
Em vez de verificar as teclas para ver se elas são todas numéricas, você itera sobre as chaves que estariam lá para uma matriz numérica e verifique se elas existem.
fonte
[1,2,null,4]
falhará, mas é matriz correta. então eu adicionei alguns aprimoramentos em stackoverflow.com/a/25206156/501831 comarray_key_exists
verificação de adição )isset()
é a ferramenta errada aqui, porque retornará false se o valor estiver definido, mas énull
, como apontado por @lazycommit.Minha solução:
array_merge
em uma única matriz reindexará todas asinteger
chaves, mas não outras. Por exemplo:Portanto, se uma lista (uma matriz não associativa) for criada
['a', 'b', 'c']
, um valor será removido e, emunset($a[1])
seguidaarray_merge
, chamado, a lista será reindexada a partir de 0.fonte
O(n)
na memória adicional usada (desde que ele criou várias novas matrizes com tantos elementos quanto$array
), a resposta não aborda a ambiguidade da pergunta que foi feita nem explica exatamente como está definindo uma lista / matriz não associativa e até se nenhum desses pontos for verdadeiro, não está claro que isso agregue algum valor em comparação com outras respostas já postadas.Após alguns testes locais de benchmarking, depuração, análise do compilador, criação de perfil e abuso do 3v4l.org para comparar outras versões (sim, recebi um aviso para parar) e comparar todas as variações que pude encontrar ...
Ofereço a você uma função de teste associativo de cenário da melhor média para o pior caso, derivada organicamente, que é, na pior das hipóteses, tão boa quanto ou melhor que todos os outros cenários de média.
Em https://3v4l.org/rkieX :
fonte
Aqui está o método que eu uso:
Observe que isso não é responsável por casos especiais como:
Desculpe, não posso ajudá-lo com isso. Também é um pouco de desempenho para matrizes de tamanho decente, pois não faz cópias desnecessárias. São essas pequenas coisas que tornam o Python e o Ruby muito mais agradáveis de se escrever ...: P
fonte
Ambos os exemplos, que obtiveram mais pontos, não funcionam corretamente com matrizes como
$array = array('foo' => 'bar', 1)
fonte
Isso também funcionaria ( demo ):
Observe que o ponto principal desta resposta é informá-lo sobre a existência
SplFixedArray
e não incentivá-lo a usar exceções para esses tipos de testes.fonte
Eu acho que a definição de uma matriz escalar irá variar de acordo com a aplicação. Ou seja, alguns aplicativos exigirão um senso mais estrito do que se qualifica como uma matriz escalar, e alguns aplicativos exigirão um senso mais flexível.
A seguir, apresento 3 métodos de rigidez variável.
fonte
Um mais rápido da fonte . Ajuste a codificação de
json_encode
(ebson_encode
). O mesmo vale para javascript Array.fonte
isset
earray_key_exists
? o último não seria suficiente?isset()
verificação aqui é completamente redundante.isset()
é mais rápido quearray_key_exists()
. veja ilia.ws/archives/…null
s, mas também não é provável que você tenha uma matriz grande o suficiente para que haja uma diferença de desempenho perceptível usando as duas verificaçõesjson_encode
, você poderia simplesmente verificar o primeiro símbolo da cadeia, retornado porjson_encode($your_arr)
- se é[
ou{
;-)Essa poderia ser a solução?
A ressalva é obviamente que o cursor da matriz é redefinido, mas eu diria que provavelmente a função é usada antes que a matriz seja atravessada ou usada.
fonte
array("a", "b")
earray("a", "b" => "B")
como apenas verifica a primeira tecla. BTW,is_long
é apenas um apelido deis_int
.[7 => 'foo', 2 => 'bar']
como uma matriz "mista" que é parcialmente, mas não "puramente" seqüencial. Parece-me um uso claramente incorreto de palavras para mim.Muitas soluções aqui são elegantes e bonitas, mas não se adaptam bem e consomem muita memória ou CPU. A maioria está criando 2 novos pontos de dados na memória com esta solução dos dois lados da comparação. Quanto maior a matriz, mais difícil e mais demorado o processo e a memória utilizados, e você perde o benefício da avaliação de curto-circuito. Eu fiz alguns testes com algumas idéias diferentes. Tentando evitar array_key_exists, pois é caro, e também evitando a criação de novos conjuntos de dados grandes para comparar. Eu sinto que esta é uma maneira simples de saber se uma matriz é seqüencial.
Você executa uma única contagem na matriz principal e armazena um único número inteiro. Em seguida, você percorre a matriz e verifica a correspondência exata enquanto itera o contador. Você deve ter de 1 a contar. Se falhar, haverá um curto-circuito, o que aumenta a performance quando é falso.
Originalmente, eu fiz isso com um loop for e verificando o isset ($ arr [$ i]), mas isso não detectará chaves nulas, o que requer array_key_exists, e como sabemos, essa é a pior função a ser usada para velocidade.
Atualizando constantemente as variáveis via foreach para verificar junto com o iterador que nunca cresce além do tamanho inteiro, vamos usar o PHP como otimização de memória, armazenamento em cache e coleta de lixo para manter o uso de recursos muito baixo.
Além disso, argumentarei que o uso de array_keys em um foreach é tolo quando você pode simplesmente executar $ key => $ value e verificar a chave. Por que criar o novo ponto de dados? Depois de abstrair as chaves da matriz, você consome mais memória imediatamente.
fonte
as respostas já são dadas, mas há muita desinformação sobre o desempenho. Eu escrevi esse pequeno script de benchmark que mostra que o método foreach é o mais rápido.
Isenção de responsabilidade: os métodos a seguir foram copiados e colados das outras respostas
resultados:
fonte
Ou você pode simplesmente usar isso:
que verificará se a matriz contém alguma chave não numérica ou:
para verificar se a matriz é estritamente sequencial (contém chaves int geradas automaticamente de 0 a n-1 )
usando esta biblioteca.
fonte
A menos que o PHP tenha um builtin para isso, você não poderá fazer isso em menos de O (n) - enumerando todas as chaves e verificando o tipo inteiro. Na verdade, você também deseja garantir que não haja buracos; portanto, seu algoritmo pode se parecer com:
Mas por que se preocupar? Suponha que a matriz seja do tipo que você espera. Caso contrário, ele simplesmente explodirá na sua cara - é uma programação dinâmica para você! Teste seu código e tudo ficará bem ...
fonte
Comparo a diferença entre as chaves da matriz e as chaves do resultado de array_values () da matriz, que sempre será uma matriz com índices inteiros. Se as chaves são as mesmas, não é uma matriz associativa.
fonte
O(n)
memória adicional quando$array
possuin
itens, e escrever em(someboolean) ? false : true
vez de!someboolean
é horrível e gratuito.