Jump to content


Photo

Comparação De Arrays Multidimensionais


  • Faça o login para participar
34 replies to this topic

#1 Bruno Augusto

Bruno Augusto

    ∙•● Restarting... ●•∙

  • Usuários
  • 1968 posts
  • Sexo:Não informado
  • Localidade:Itajubá

Posted 31/01/2010, 12:52

Vejam a foto:

Posted Image
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. :P

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 Paulo Freitas

Paulo Freitas

    ××××××× LRU #456504 ××××××× ××××××× LRM #364686 ×××××××

  • Ex-Admins
  • 5612 posts
  • Sexo:Masculino
  • Localidade:Campinas - SP

Posted 31/01/2010, 13:16

Acho que é isso:

<?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 Bruno Augusto

Bruno Augusto

    ∙•● Restarting... ●•∙

  • Usuários
  • 1968 posts
  • Sexo:Não informado
  • Localidade:Itajubá

Posted 31/01/2010, 13:54

Pelo manual, strcmp() compara e retorna maior, menor ou igual a zero. Como que ela pode diferenciar MD5 (índice checksum)?

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 Paulo Freitas

Paulo Freitas

    ××××××× LRU #456504 ××××××× ××××××× LRM #364686 ×××××××

  • Ex-Admins
  • 5612 posts
  • Sexo:Masculino
  • Localidade:Campinas - SP

Posted 31/01/2010, 13:59

Pelo manual, strcmp() compara e retorna maior, menor ou igual a zero. Como que ela pode diferenciar MD5 (índice checksum)?

Como callback da função array_udiff() funciona como uma comparação de string qualquer. É isso que o parâmetro de callback espera:

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.

Se forem iguais, não retorna (pois o objetivo da função é o oposto), se for qualquer outra coisa, retorna.

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?

Boiei. :huh:

[]’sAté mais

#5 Bruno Augusto

Bruno Augusto

    ∙•● Restarting... ●•∙

  • Usuários
  • 1968 posts
  • Sexo:Não informado
  • Localidade:Itajubá

Posted 01/02/2010, 11:38

Entendido, 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.

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 Paulo Freitas

Paulo Freitas

    ××××××× LRU #456504 ××××××× ××××××× LRM #364686 ×××××××

  • Ex-Admins
  • 5612 posts
  • Sexo:Masculino
  • Localidade:Campinas - SP

Posted 01/02/2010, 12:50

Entendido, 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.

Sim, ele aparece no 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. ;-)

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.

Muda de servidor. :rotfl2::

Brincadeira.

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.

Hmm, precisaria ver como tu está fazendo e qual erro em específico foi gerado.

[]’sAté mais

#7 Bruno Augusto

Bruno Augusto

    ∙•● Restarting... ●•∙

  • Usuários
  • 1968 posts
  • Sexo:Não informado
  • Localidade:Itajubá

Posted 02/02/2010, 14:46

Acho que nã importa como estou fazendo, porque não deu certo mesmo :(

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 Paulo Freitas

Paulo Freitas

    ××××××× LRU #456504 ××××××× ××××××× LRM #364686 ×××××××

  • Ex-Admins
  • 5612 posts
  • Sexo:Masculino
  • Localidade:Campinas - SP

Posted 02/02/2010, 15:01

Acho que nã importa como estou fazendo, porque não deu certo mesmo :(

Importa 100%. Não dar certo significa estar fazendo algo de errado.

A tentatyiva do modelo clássico foi, informando como terceiro parâmetro para array_udiff():
array( $this, 'metodo_de_comparacao' )

E o método de comparação é...? Sim, o código do método.

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. :huh:

[]’sAté mais

#9 Bruno Augusto

Bruno Augusto

    ∙•● Restarting... ●•∙

  • Usuários
  • 1968 posts
  • Sexo:Não informado
  • Localidade:Itajubá

Posted 02/02/2010, 15:15

Falei 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.

#10 Paulo Freitas

Paulo Freitas

    ××××××× LRU #456504 ××××××× ××××××× LRM #364686 ×××××××

  • Ex-Admins
  • 5612 posts
  • Sexo:Masculino
  • Localidade:Campinas - SP

Posted 02/02/2010, 15:32

Falei 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.

Justamente por isso que preciso saber exatamente como você está usando a função array_udiff(). Em código. Eu já imagino o problema, mas não dá pra tirar conclusões precipitadas.

[]’sAté mais

#11 Bruno Augusto

Bruno Augusto

    ∙•● Restarting... ●•∙

  • Usuários
  • 1968 posts
  • Sexo:Não informado
  • Localidade:Itajubá

Posted 02/02/2010, 15:43

Então tudo bem, se faz questão...

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 Paulo Freitas

Paulo Freitas

    ××××××× LRU #456504 ××××××× ××××××× LRM #364686 ×××××××

  • Ex-Admins
  • 5612 posts
  • Sexo:Masculino
  • Localidade:Campinas - SP

Posted 02/02/2010, 16:06

O que não está funcionando e por que ocorre mesmo esse erro de ->? 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 Bruno Augusto

Bruno Augusto

    ∙•● Restarting... ●•∙

  • Usuários
  • 1968 posts
  • Sexo:Não informado
  • Localidade:Itajubá

Posted 05/02/2010, 16:17

Opa, esse $this -> teste() foi falha minha.

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 -1
O 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 Paulo Freitas

Paulo Freitas

    ××××××× LRU #456504 ××××××× ××××××× LRM #364686 ×××××××

  • Ex-Admins
  • 5612 posts
  • Sexo:Masculino
  • Localidade:Campinas - SP

Posted 05/02/2010, 19:13

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.

Isso não faz a menor diferença; ambos são considerados strings diferentes.

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.

Está documentado no manual:

Returns < 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal.

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).

...

Pois é, a velha ordem lexicográfica. Vale notar que a comparação é binary safe e case sensitive.

Isso não é certo para esse caso.

Por quê?

Relembrando o retorno da função array_udiff():

Returns an array containing all the values of array1 that are not present in any of the other arguments.

Repetindo o que eu já disse anteriormente: para a função array_udiff() não interessa o +1 e -1 da função strcmp(). Interessa o 0.

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() ?

Não entendi o problema com a função strcmp(), ela funciona exatamente para o que foi proposto.

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

Como dito acima, não faz diferença alguma, diferente é diferente de qualquer jeito.

[]’sAté mais

#15 Bruno Augusto

Bruno Augusto

    ∙•● Restarting... ●•∙

  • Usuários
  • 1968 posts
  • Sexo:Não informado
  • Localidade:Itajubá

Posted 06/02/2010, 14:12

Então porque não funciona? Fiz igualzinho o seu e funcionou porque eu troquei o último caractere do hash.

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)

IPB Skin By Virteq