Comparação De Arrays Multidimensionais
#1
Posted 31/01/2010, 12:52
O primeiro array vêm via HTTP POST, o segundo é um cache de arquivo depois de desserializado.
Isso faz parte de um sistema de atualizações onde é enviado para o meu servidor a lista de arquivos que uma instalação do meu sistema tem e é comparada com outra lista, pré-existente.
Combinando em nomes de arquivo, mas sendo diferente em checksum, entra para lista final, siginificando que deve haver atualização daquele arquivo.
Eu fiz dois foreach's aninhados, cada um sobre um dos arrays, com if's aninhados que verificam o valor das chaves e incluem em outro array, para ser retornado, se houver diferença.
Mas são arrays bem grandes (475 itens para ser exato) e fazer loop dentro de loop acaba ficando muito lento. Ainda mais que é um serviço, se ficar lento minha banda vai pra cucuia.
Como eu faço para comparar? Anteriormente os índices filepaths eram diferentes a nível de 3 diretórios. Mexi daqui e mexi dali até fazer o filepath do cache ser igual ao vindo via HTTP POST, mas ainda não consigo comparar por ser multimensional.
A minha tentativa deu certo? Deu. Mas ficou inflexível e, além de comparar essas diferenças, preciso reutilizar o código para obter também os novos arquivos que eu crio os quais uma atualização possa depender. Assim evito a cada pequena correção, obrigar o usuário a baixar um release de mais ou menos 4MB.
#2
Posted 31/01/2010, 13:16
<?php // PHP 5.3+ $diff = array_udiff($array1, $array2, function ($array1, $array2) { return strcmp($array1['checksum'], $array2['checksum']); }); // PHP 5+ $diff = array_udiff($array1, $array2, create_function('$array1, $array2', 'return strcmp($array1["checksum"], $array2["checksum"]);')); ?>PS: Atençao à ordem dos dois primeiros parâmetros da função
array_udiff()
. Se retornar o oposto do que você quer, basta invertê-los.[]’sAté mais
#3
Posted 31/01/2010, 13:54
E mais: Quando comparando para obter a lista de arquivo novos, no array POST não haverá o índice checksum correspondente. Essa associação idéias que estou fazendo não compete, certo? Se não existir, vai ser incluindo de todo jeito?
#4
Posted 31/01/2010, 13:59
Como callback da funçãoPelo manual, strcmp() compara e retorna maior, menor ou igual a zero. Como que ela pode diferenciar MD5 (índice checksum)?
array_udiff()
funciona como uma comparação de string qualquer. É isso que o parâmetro de callback espera:Se forem iguais, não retorna (pois o objetivo da função é o oposto), se for qualquer outra coisa, retorna.The user supplied callback function is used for comparison. It must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
Boiei.E mais: Quando comparando para obter a lista de arquivo novos, no array POST não haverá o índice checksum correspondente. Essa associação idéias que estou fazendo não compete, certo? Se não existir, vai ser incluindo de todo jeito?
[]’sAté mais
#5
Posted 01/02/2010, 11:38
Se eu desenvolver um arquivo a mais do que o release corrente do usuário, seja para adicionar um recurso ou complementar um recurso existente atualizado, no array pós unserialize() (o meu cache) eu terei uma entrada extra, (a exemplo, a de nº. 475).
Ao comparar com esse callback usando strcmp(), esse índice será incluído no array das diferenças? Pois ele não existe no outro array, vindo via HTTP POST / AJAX POST, sendo assim não tem com quem ser comparado.
Outra coisa. O modelo 5.3 não posso usar pois são poucos servidores no Brasil com essa versão do PHP instalado, então preciso usar a segunda.
Porém, a menos que eu tenho digitado algo errado, deu um erro de -> (esqueci o nome ^^), pois, o segundo arrayu é uma propriedade da classe.
Pensei em usar o modelo "clássico" de array_udiff(), mas deu erro de tipos.
#6
Posted 01/02/2010, 12:50
Sim, ele aparece noEntendido, vou exemplificar o último ponto.
Se eu desenvolver um arquivo a mais do que o release corrente do usuário, seja para adicionar um recurso ou complementar um recurso existente atualizado, no array pós unserialize() (o meu cache) eu terei uma entrada extra, (a exemplo, a de nº. 475).
Ao comparar com esse callback usando strcmp(), esse índice será incluído no array das diferenças? Pois ele não existe no outro array, vindo via HTTP POST / AJAX POST, sendo assim não tem com quem ser comparado.
array
de diferença. Quanto ao
unserialize()
, não existe a possibilidade de usar json_decode()
? Esta função está disponível no PHP 5.2+. Fica bem mais fácil de trabalhar com o Javascript. Muda de servidor.Outra coisa. O modelo 5.3 não posso usar pois são poucos servidores no Brasil com essa versão do PHP instalado, então preciso usar a segunda.
Brincadeira.
Hmm, precisaria ver como tu está fazendo e qual erro em específico foi gerado.Porém, a menos que eu tenho digitado algo errado, deu um erro de -> (esqueci o nome ^^), pois, o segundo array é uma propriedade da classe.
Pensei em usar o modelo "clássico" de array_udiff(), mas deu erro de tipos.
[]sAté mais
#7
Posted 02/02/2010, 14:46
Já que uma propriedade, com $this, não possa entrar como parte de um create_function() (aparentemente), criei duas variáveis para armazenar os valores delas para "passar".
Vou postar exatamente o código que estou usando para preencher essa propriedade manualmente (meu Modo Debug), juntamente com meu ahe serialziado.
$this -> data = array( 'service' => 'WordPress', 'product' => 'Corporative', 'uType' => 'fixes', 'files' => array( array( 'filepath' => 'admin/application/controllers/AdvertisementController.php', 'checksum' => '42ab4fc2af91337be03ce1cb7f4fd837', 'filename' => 'AdvertisementController.php' ), array( 'filepath' => 'admin/application/controllers/HomeController.php', 'checksum' => '28cbf3c60752631a3fa87e427e35afb6', 'filename' => 'HomeController.php' ), array( 'filepath' => 'admin/library/Zend/Controller/Request/Exception.php', 'checksum' => 'ef458d8a75cc650d9cbf90ff89df9012', 'filename' => 'Exception.php', ), ) );
Meu cache Serializado
O que não funcioou foi que, informando primeiro o cache e depois o POST, retornou todo o cache SEM alguns elementos (perceptíveis por "pular" alguns índices) e, o contrário, primeiro o POST, retornou vazio.
A tentatyiva do modelo clássico foi, informando como terceiro parâmetro para array_udiff():
array( $this, 'metodo_de_comparacao' )
Quem sabe assim melhore o entendimento.
#8
Posted 02/02/2010, 15:01
Importa 100%. Não dar certo significa estar fazendo algo de errado.Acho que nã importa como estou fazendo, porque não deu certo mesmo
E o método de comparação é...? Sim, o código do método.A tentatyiva do modelo clássico foi, informando como terceiro parâmetro para array_udiff():
array( $this, 'metodo_de_comparacao' )
A linha do uso da função
array_udiff()
também é super importante.Não dá pra ter idéia do que pode estar ocorrendo ou deixando de ocorrer sem esses detalhes.
[]’sAté mais
#9
Posted 02/02/2010, 15:15
Eu postei como estou faznedo, tanto é que você até citou perguntando o que tinha no método. Ea resposta para esse método é aquilo que você passou na primeira resposta.
A diferença é que, colocando como método próprio, as variáeis têm nome certo. E são acessadas como variáveis e não como propriedades, daí o erro de -> não ocorre.
Mas ainda assim não funciona.
#10
Posted 02/02/2010, 15:32
Justamente por isso que preciso saber exatamente como você está usando a funçãoFalei mais no desabafo ^^
Eu postei como estou faznedo, tanto é que você até citou perguntando o que tinha no método. Ea resposta para esse método é aquilo que você passou na primeira resposta.
A diferença é que, colocando como método próprio, as variáeis têm nome certo. E são acessadas como variáveis e não como propriedades, daí o erro de -> não ocorre.
Mas ainda assim não funciona.
array_udiff()
. Em código. Eu já imagino o problema, mas não dá pra tirar conclusões precipitadas.[]sAté mais
#11
Posted 02/02/2010, 15:43
Para minha instalação local, que´é 5.3+
$diff2 = array_udiff( $a1, $a2, function( $a1, $a2 ) { return strcmp( $a1['checksum'], $a2['checksum'] ); } );
Para adequar aos servidores de quem vier a utilizar:
$diff = array_udiff( $a1, $a2, create_function( '$a1, $a2', 'return strcmp( $a1["checksum"], $a2["checksum"] );' ) );
Para ambas as situações:
$a1 = $this -> teste( $cache ); $a2 = $this -> teste( $this -> data['files'] );E o modo "clássico":
$diff = array_udiff( $a1, $a2, array( $this, 'computeDiff' ) ); ... private function computeDiff( array $cache, array $sent ) { return strcmp( $cache["checksum"], $sent["checksum"] ); }
#12
Posted 02/02/2010, 16:06
->
? Não entendi a finalidade de $this->teste()
também...Testei assim:
<?php class foo { public function __construct() { $this->data = array( 'service' => 'WordPress', 'product' => 'Corporative', 'uType' => 'fixes', 'files' => array( array( 'filepath' => 'admin/application/controllers/AdvertisementController.php', 'checksum' => '42ab4fc2af91337be03ce1cb7f4fd837', 'filename' => 'AdvertisementController.php' ), array( 'filepath' => 'admin/application/controllers/HomeController.php', 'checksum' => '28cbf3c60752631a3fa87e427e35afb5', // checksum modificado 'filename' => 'HomeController.php' ), array( 'filepath' => 'admin/library/Zend/Controller/Request/Exception.php', 'checksum' => 'ef458d8a75cc650d9cbf90ff89df9012', 'filename' => 'Exception.php', ), ) ); } public function diff($cache) { // Inverti a ordem dos 2 primeiros parâmetros para retornar a diferença com base no $this->data. // O contrário retorna a diferença com base no $cache (retornaria o mesmo arquivo, mas com o checksum do cachê). return array_udiff($this->data['files'], $cache, array($this, 'computeDiff')); } private function computeDiff(array $cache, array $sent) { return strcmp($cache["checksum"], $sent["checksum"]); } } $cache = array( array( 'filepath' => 'admin/application/controllers/AdvertisementController.php', 'checksum' => '42ab4fc2af91337be03ce1cb7f4fd837', 'filename' => 'AdvertisementController.php' ), array( 'filepath' => 'admin/application/controllers/HomeController.php', 'checksum' => '28cbf3c60752631a3fa87e427e35afb6', 'filename' => 'HomeController.php' ), array( 'filepath' => 'admin/library/Zend/Controller/Request/Exception.php', 'checksum' => 'ef458d8a75cc650d9cbf90ff89df9012', 'filename' => 'Exception.php', ), ); $foo = new foo; var_dump($foo->diff($cache)); /* array(1) { [1]=> array(3) { ["filepath"]=> string(48) "admin/application/controllers/HomeController.php" ["checksum"]=> string(32) "28cbf3c60752631a3fa87e427e35afb5" ["filename"]=> string(18) "HomeController.php" } } */ ?>[]’sAté mais
#13
Posted 05/02/2010, 16:17
Queria limpar um pedacinho de filepath sem ter de recriar o cache. Vou testar o modo como fez porque, visualmente está igualzinho o que eu estava fazendo.
Funcionou... Ou quase...
Estritamente do jeito que você postou, porém, dentro da minha classe ao invés de uma nova,
retornou o mesmo que você obteve.
Mas, como isso é um parte de um sistema de atualização, em linha de produção, o hash enviado
(que você populou no construtor) seria um completamente diferente, ao invés de mudar um único caractere.
Em paralelo, mantenho outro script de testes que me informa o hash real dos arquivos, isto é, a mesma
informação que o serviço receberá via POST para comparar.
Esse hash, nesse momento, é 949b6dc2061907b0fd5af3a7ed088bf9.
Oras, o que strcmp() faz na realidade?
Pergunto isso porque, depurando com var_dump(), obtive:
var_dump( strcmp( '28cbf3c60752631a3fa87e427e35afb5', '28cbf3c60752631a3fa87e427e35afb6' ) ); // int(-1) var_dump( strcmp( '28cbf3c60752631a3fa87e427e35afb6', '28cbf3c60752631a3fa87e427e35afb5' ) ); // int(1) var_dump( strcmp( '28cbf3c60752631a3fa87e427e35afb6', '949b6dc2061907b0fd5af3a7ed088bf9' ) ); // int(-1)Pelo que entendi, no primeiro e terceiro caso a primeira string é menor que a segunda, enquanto no segundo é maior.
Mas menor e maior em quê? Me parece que strcmp() está comparando caractere a caractere e, quando encontra uma diferença,
determina se é maior ou menor (quando números) ou "vem antes ou depois" (quando letras). Acompanhe o esquema:
2 8 c b f 3 c 6 0 7 5 2 6 3 1 a 3 f a 8 7 e 4 2 7 e 3 5 a f b 5 2 8 c b f 3 c 6 0 7 5 2 6 3 1 a 3 f a 8 7 e 4 2 7 e 3 5 a f b 6 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = < // Resultado: Menor, logo -1O esquema está ilustrado pelos sinais de comparação
Como dá para perceber, o teste foi se desenrolou até que alguma diferença ocorreu e só então determinou como sendo
um hash diferente.
Já no terceiro caso, o primeiro caractere da primeira string é 2 e na segunda 9. Só a partir
desse caractere já deu diferença, sendo caracterizado como menor.
Eu não acredito que estou tão errado nessa afirmação pois, nos comentários do manual, um tal de Colin expõe
um exemplo que também usa strcmp() para comparar strings e, quando elas são exatamente iguais, ele também
obteve um array vazio.
Isso não é certo para esse caso.
Com tudi isso, fica a pergunta: Se para funcionar, array_udiff() requer que a função ou método de callback retorne
-1, 0 ou 1 e, para comparar essas strings essa exigência acaba se tornando uma impossibilidade, array_udiff() é a melhor
função para a tarefa?
Se o problema não for com array_udiff() e sim com a escolha de strcmp() (que não só eu achei problemática) para a
tarefa, há como fazer outra função retorna o esquema requerido poe array_udiff() ?
Enfim, por consideração a uma possível solução que não exija a desserialização do array de cache, vou postar as
estruturas correntes com seus respectivos hashes atualizados.
O registro com hash diferente, agora, fica para AdvertisementController, bem mais diferente que um único caractere
// $cache (regenerado) Array ( [0] => Array ( [filepath] => C:/Program Files/Zend/Apache2/htdocs/repository/wordpress/themes/Corporative/admin/application/controllers/AdvertisementController.php [checksum] => 928c5e3bd0c5f8d3613fce7a3bdc9dac [filename] => AdvertisementController.php ) [1] => Array ( [filepath] => C:/Program Files/Zend/Apache2/htdocs/repository/wordpress/themes/Corporative/admin/application/controllers/HomeController.php [checksum] => 28cbf3c60752631a3fa87e427e35afb6 [filename] => HomeController.php ) [2] => Array ( [filepath] => C:/Program Files/Zend/Apache2/htdocs/repository/wordpress/themes/Corporative/admin/library/Zend/Controller/Request/Exception.php [checksum] => ef458d8a75cc650d9cbf90ff89df9012 [filename] => Exception.php ) ) // $POST Array ( [0] => Array ( [filepath] => C:/Program Files/Zend/Apache2/htdocs/repository/wordpress/themes/Corporative/admin/application/controllers/AdvertisementController.php [checksum] => 42ab4fc2af91337be03ce1cb7f4fd837 [filename] => AdvertisementController.php ) [1] => Array ( [filepath] => C:/Program Files/Zend/Apache2/htdocs/repository/wordpress/themes/Corporative/admin/application/controllers/HomeController.php [checksum] => 28cbf3c60752631a3fa87e427e35afb6 [filename] => HomeController.php ) [2] => Array ( [filepath] => C:/Program Files/Zend/Apache2/htdocs/repository/wordpress/themes/Corporative/admin/library/Zend/Controller/Request/Exception.php [checksum] => ef458d8a75cc650d9cbf90ff89df9012 [filename] => Exception.php ) )
#14
Posted 05/02/2010, 19:13
Isso não faz a menor diferença; ambos são considerados strings diferentes.Mas, como isso é um parte de um sistema de atualização, em linha de produção, o hash enviado
(que você populou no construtor) seria um completamente diferente, ao invés de mudar um único caractere.
Está documentado no manual:Oras, o que strcmp() faz na realidade?
Pergunto isso porque, depurando com var_dump(), obtive:var_dump( strcmp( '28cbf3c60752631a3fa87e427e35afb5', '28cbf3c60752631a3fa87e427e35afb6' ) ); // int(-1) var_dump( strcmp( '28cbf3c60752631a3fa87e427e35afb6', '28cbf3c60752631a3fa87e427e35afb5' ) ); // int(1) var_dump( strcmp( '28cbf3c60752631a3fa87e427e35afb6', '949b6dc2061907b0fd5af3a7ed088bf9' ) ); // int(-1)Pelo que entendi, no primeiro e terceiro caso a primeira string é menor que a segunda, enquanto no segundo é maior.
Returns < 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal.
Pois é, a velha ordem lexicográfica. Vale notar que a comparação é binary safe e case sensitive.Mas menor e maior em quê? Me parece que strcmp() está comparando caractere a caractere e, quando encontra uma diferença,
determina se é maior ou menor (quando números) ou "vem antes ou depois" (quando letras).
...
Por quê?Isso não é certo para esse caso.
Relembrando o retorno da função
array_udiff()
:Repetindo o que eu já disse anteriormente: para a funçãoReturns an array containing all the values of array1 that are not present in any of the other arguments.
array_udiff()
não interessa o +1
e -1
da função strcmp()
. Interessa o 0
.Não entendi o problema com a funçãoCom tudi isso, fica a pergunta: Se para funcionar, array_udiff() requer que a função ou método de callback retorne
-1, 0 ou 1 e, para comparar essas strings essa exigência acaba se tornando uma impossibilidade, array_udiff() é a melhor
função para a tarefa?
Se o problema não for com array_udiff() e sim com a escolha de strcmp() (que não só eu achei problemática) para a
tarefa, há como fazer outra função retorna o esquema requerido poe array_udiff() ?
strcmp()
, ela funciona exatamente para o que foi proposto.Como dito acima, não faz diferença alguma, diferente é diferente de qualquer jeito.Enfim, por consideração a uma possível solução que não exija a desserialização do array de cache, vou postar as
estruturas correntes com seus respectivos hashes atualizados.
O registro com hash diferente, agora, fica para AdvertisementController, bem mais diferente que um único caractere
[]sAté mais
#15
Posted 06/02/2010, 14:12
Mas quando eu troquei o hash inteiro deixou de ser incluído na listagem?
1 user(s) are reading this topic
0 membro(s), 1 visitante(s) e 0 membros anônimo(s)