Arrays em AdvPL – Parte 02

Introdução

No post anterior, vimos o conceito e propriedades de um Array. Agora, vamos ver algumas funções genérias e operadores que podem ser usados com variáveis do tipo ARRAY, e as demais funções da linguagem AdvPL específicas para manipular dinamicamente conteúdos e dimensões de um Array.

Funções genéricas

Para saber quantos elementos têm um array, usamos a função LEN(). Ela também é usada para descobrir, por exemplo, quantos caracteres existem dentro de uma variável ou expressão do tipo “C” caractere no AdvPL.

Operadores

Um array apenas permite o uso dos comparadores de igualdade (=) ou diferença (<> ou !=) entre uma expressão do tipo “A” Array e outra expressão do tipo “A” Array ou “U” NIL.

Quando comparamos uma variável do tipo Array com outro Array, usando os operadores de igualdade (=) ou diferença”( <> ou != ), a comparação não leva em conta o conteúdo ou tamanho do Array. Na verdade, o operador de igualdade somente será .T.  (verdadeiro) entre duas variáveis do tipo  “A” Array se os arrays forem uma referência ao mesmo conteúdo. Por exemplo:

User Function aTest8()
aTeste1 := {1,2,3}
aTeste2 := aTeste1
If aTeste1 = aTeste2
   MsgInfo("aTeste1 e aTeste2 são referencias ao mesmo conteúdo")
Else
  MsgStop("*** Resultado inesperado ***")
Endif
Return

Agora, vamos alterar um pouco o exemplo:

User Function aTest9()
aTeste1 := {1,2,3}
aTeste2 := {1,2,3} 
If aTeste1 = aTeste2
  MsgStop("*** Resultado inesperado ***")
Else
  MsgInfo("aTeste1 e aTeste2 são referencias ao mesmo conteúdo")
Endif
Return

Reparem que aTeste1 e aTeste2 possuem a mesma quantidade de elementos, e cada posição do array é exatamente igual a mesma posição do outro Array. Mas a operação de igualdade FALHOU, pois são dois Arrays totalmente independentes, um não é referência do outro. 

Se você precisa comparar se o conteúdo de dois arrays é o mesmo, mas não necessariamente os arrays têm a mesma referência, você pode construir uma função em AdvPL para realizar a comparação. Veja um exemplo abaixo, que compara dois arrays quaisquer.

STATIC Function ACompare(xVal1,xVal2)
Local cT1 := valtype(xVal1)
Local cT2 := valtype(xVal2)
Local nI 

If cT1 != cT2
	// Os tipos são diferentes ? 
	// A comparação retorna .F.
	Return .F.
Endif

If cT1 != 'A'
	// Nao estamos comparando arrays.
	// Utiliza o operador de igualdade 
	// entre as variáveis 
	Return xVal1 == xVal2
Endif

If len(xVal1) != len(xVal2)
	// Estamos comparando arrays 
	// O tamanho deles é diferente ? 
	// A comparação retorna .F. 
	Return .F.
Endif

// Compara cada elemento do array recursivamente 
For nI := 1 to len(xVal1)
	If !ACompare( xVal1[nI] , xVal2[nI] )
		// No primeiro elemento que a compparação 
		// não for verdadeira, já retorna .F. 
		Return .F. 
	Endif
Next

// Chegou até o final, os elementos 
// dos dois arrays tem o mesmo conteúdo 
Return .T.

Com esta função, conseguimos dizer se dois arrais que não são referência um do outro possuem a mesma estrutura e conteúdo. Vamos fazer um fonte de testes, com dois arrays com mesmo conteúdo, vide abaixo:

User function aTest9()
Local aTeste1 := {1,2,3}
Local aTeste2 := {1,2,3}
Local cMsg := ''
If aTeste1 == aTeste2
	cMsg += 'Os arrays referenciam o mesmo conteúdo' 
Else
	If ACompare(aTeste1,aTeste2)
		cMsg += 'Os arrays são iguais' 
	Else
		cMsg += 'Os arrays são diferentes' 
	Endif
Endif
MsgInfo(cMsg)
Return

O resultado a ser obtido é “Os arrays são iguais”, pois eles não são referência um do outro, mas tem exatamente o mesmo número de elementos, estrutura e conteúdo.

Funções de Manipulação de Arrays

Segue abaixo a lista de funções do AdvPL feitas para trabalhar com manutenção dos dados e consumo dos elementos de um array. Todas estas funções estão documentadas na TDN, cada uma das funções abaixo possui um link para a documentação oficial

Função AADD

Usando a função AADD, podemos acrescentar um novo elemento dentro de um Array já existente.

Sintaxe: AADD( <aArray> , <xValor>) 
-- aArray é o Array a ter um novo elemento acrescentado
-- xValor é a expressão ou valor a ser acrescentado em aArray

Por exemplo, vamos ver uma outra forma de criar o array aCores:

// Ao invés de:
aCores := { {0,"Preto"}, {1,"Azul"} , {2,"Verde"} }

// Podemos usar:
aCores := {}
aadd(aCores, {0,"Preto"} )
aadd(aCores, {1,"Azul"})
aadd(aCores, {3,"Verde"})

A função AADD aumenta (expande) o tamanho do array onde a informação foi inserida, e o novo elemento é SEMPRE inserido no final do Array, passando naturalmente a ser o último elemento do Array — até que um novo elemento seja acrescentado.

Já viu como isso é útil, certo ? Se você têm por exemplo uma lista de categorias dentro de uma tabela, e precisa ou quer mostrar ela em um componente de lista de interface, que trabalha com Array, você pode fazer uma consulta na base de dados e adicionar cada registro encontrado em um Array na memória para posterior utilização.

Função ACLONE

Lembra-se que simplesmente atribuir um array para outra variável faz uma referência ao array original, todos apontam para o mesmo conteúdo? Então, usando esta função, ela duplica os conteúdos e a estrutura do array, criando uma cópia idêntica do array original, com a mesma estrutura e a cópia dos conteúdos. Desta forma, eles passam a ser dois arrays independentes, com conteúdo igual.

Sintaxe: aDestino := ACLONE(aOrigem)
-- aDestino é o array com a cópia dos dados e estrutura de aOrigem
-- aOrigem é o array que será "clonado"

Vamos aproveitar algumas funções anteriores e criar um novo exemplo:

USER Function aTest11()
Local aTeste1, aTeste2 , aTeste3 
aTeste1 := {1,2,3}
aTeste2 := aTeste1
aTeste3 := aClone(aTeste1)
If aTeste1 == aTeste2
	MsgInfo("aTeste1 e aTeste2 SãO referências")
Else
	MsgInfo("aTeste1 e aTeste2 NãO SãO referências")
Endif
If aTeste2 == aTeste3
	MsgInfo("aTeste2 e aTeste3 SãO referências")
Else
	MsgInfo("aTeste2 e aTeste3 NãO SãO referências")
Endif
If ACompare( aTeste2 , aTeste3 )
	MsgInfo("aTeste2 e aTeste3 TEM conteúdo idêntico")
Else
	MsgInfo("aTeste2 e aTeste3 NAO TEM conteúdo idêntico")
Endif
Return

Pelo que vimos até agora, podemos prever que o resultado deste programa será:

  • aTeste1 e aTeste2 SãO referências
  • aTeste2 e aTeste3 NãO SãO referências
  • aTeste2 e aTeste3 TEM conteúdo idêntico

Observações 

Os tipos AdvPL “B” Codeblock e “O” Objeto também são tratados com referência implícita. Logo, ao atribuir uma variável contendo um Objeto ou Codeblock para outra variável ou para um elemento de array, o CodeBlock é referenciado, e não duplicado, INCLUSIVE quando você usa a função ACLONE() — Ela quebra a referência entre o array de origem e o de destino, porém os elementos do tipo Objeto e ClodeBlock não são “duplicados”, eles passam a ser referenciados pelos dois Arrays. Veja o exemplo abaixo, onde usamos um CodeBlock como um elemento do Array:

USER Function aTest12()
Local aTeste1, aTeste2 
Local bBLock := {|x| conout(x) }
aTeste1 := { bBLock  }
aTeste2 := aClone(aTeste1)
If aTeste1 == aTeste2
	MsgStop("Os arrays são referências")
Else
	MsgStop("Os arrays nao são referências")
Endif
If aTeste1[1] == aTeste2[1]
	MsgStop("O CodeBlock é referência")
Else
	MsgStop("O CodeBlock não é referência")
Endif
Return

O resultado deste programa será:

  • Os arrays não são referências
  • O CodeBlock é referência

Um Objeto e/ou um CodeBlock não são copiados ou duplicados, mas passam a serem referenciados por elementos de arrays distintos.

Função ACOPY

Através da função ACOPY, podemos copiar um ou mais elementos de um array de origem para um array de destino. Veja abaixo a sintaxe da função:

aCopy( <aOrigem>, <aDestino> , [nInicio] , [nQuant] , [nPosDest] )
-- aOrigem é o array de onde serão copiados os elementos
-- aDestino é o array para onde serão copiados os elementos
-- nInicio (default=1) indica a partir de qual elemento do array aOrigem que a cópia será iniciada
-- nQuant (default=tudo) indica quantos elementos do array de origem serão copiados para aDestino
-- aPosDest (default=1) indica a posição inicial do array de destino a partir de qual os elementos serão copiados

Observações

  • A função ACOPY() não mexe no tamanho ou quantidade de elementos do array de destino. Logo, o array de destino deve ter elementos suficientes para receber os elementos do array de origem. Somente serão copiados a quantidade de elementos que “cabem” no array de destino. Caso não haja espaço suficiente, os elementos excedentes serão ignorados na cópia, e a função não emite nenhum erro ou advertência.
  • Caso os elementos do array de origem sejam Arrays — no caso de um Array multidimensional — os elementos do array de origem serão REFERENCIADOS, e não CLONADOS. Internamente a função ACOPY() faz simplesmente atribuição do conteúdo dos elementos do array de origem para o array de destino.
  • A vantagem de uso da função ACOPY quando necessária é que você não precisa fazer um LOOP para fazer as atribuições, você apenas parametriza a função e ela realiza o loop e as atribuições internamente.

Vamos ao exemplo:

USER Function aTest13()
Local aOrigem := {"Um","Dois","Três"}
Local aDestino := {NIL,NIL}

aCopy(aOrigem,aDestino)

conout("Tamanho do destino .... "+cValToChar(len(aDestino)))
conout("Primeiro elemento do destino ... "+aDestino[1])
conout("Segundo elemento do destino .... "+aDestino[2])

Return

Baseado no comportamento esperado da função, o tamanho do array de destino após a cópia ainda será de dois elementos, e apenas os dois primeiros elementos de aOrigem serão copiados. O conteúdo já existente nos elementos do array de destino serão sobrescritos com o conteúdo dos elementos de origem. Agora vamos ver um exemplo usando todos os parâmetros:

USER Function aTest14()
Local aOrigem := {"Amarelo","Azul","Vermelho"}
Local aDestino := {"Verde",NIL,NIL,"Branco"}

// Partindo do array aOrigem, para o Array aDestino, 
// a partir do primeiro elemento, copie dois elementos 
// para o array aDestino, a partir da posição 2
aCopy(aOrigem,aDestino,1,2,2)

conout("Tamanho do destino ....... "+cValToChar(len(aDestino)))
conout("1o elemento do destino ... "+aDestino[1])
conout("2o elemento do destino ... "+aDestino[2])
conout("3o elemento do destino ... "+aDestino[3])
conout("4o elemento do destino ... "+aDestino[4])

Return

O resultado do programa acima será as cores da bandeira do Brasil. Os dois primeiros elementos do array aOrigem (“Amarelo” e ‘Azul”) serão copiados a partir do segundo elemento do array aDestino.

Função ADEL

Como o nome sugere, a função ADEL permite apagar — ou deletar — uma posição do array informado como parâmetro. Vamos a sintaxe:

aDel (aArray , nPos)
-- aArray é o array a ter um elemento apagado
-- nPos é a posição do elemento a ser apagado

Observações

  • Deve ser informada a posição do elemento a ser apagado — vale lembrar que os arrays em AdvPL começam na posição 1
  • A função ADEL não mexe no tamanho do array. Ela remove o elemento da posição informada, e o último elemento passa a ser NIL.
  • Como os elementos são acessados pela sua posição no array, ao remover uma posição, o elemento da próxima posição passa a ocupar esta posição, progressivamente até o final do array, onde o último elemento será NIL.
  • Apagar um elemento do array não significa destruir seu conteúdo, mas sim eliminar aquela posição do array que fazia referência para aquele conteúdo.

Por exemplo:

aDados := {1,2,3}
// Apagando a segunda posição 
aDel(aDados,2) 
// O array agora passou a ser { 1, 3, NIL}

Após apagar um elemento do Array, se eu quiser reduzir seu tamanho, eu posso usar a função ASIZE().

Função AEVAL

Esta função é bem prática, você informa um Array e um Codebloc, e a função AEVAL executa — faz um Eval() — do bloco de código para cada elemento do array, passando como parâmetro para o bloco aquele elemento. Vamos a sintaxe:

aEval( <aArray> , <bBloco> , [nInicio] , [nQuant]) 
-- aArray é o array a ser avaliado
-- bBloco é o Codeblock a ser executado 
-- nInicio (default=1) indica a primeira posição do Array a ser considerada para a execução
-- nQuant ( default=tudo) indica a quantidade de elementos a partir do inicial que serão avaliados

Vamos a um exemplo:

USER Function aTest16()
Local aTeste := { "Um" , "Dois" , "Três", "Quatro" }
Local bBLoco := {|x| conout(x) }

// Axecute para o array aTeste, o bloco de código 
// bBloco, a partir do segundo elemento, 
// executando apenas para dois elementos
aEval(aTeste,bBloco,2,2)

Return

O bloco de código declarado ” {|x| conout(x) } ” recebe o elemento atual do array como parâmetro na variável “x”, e executa a função conout() passando “x” como parâmetro. Desta forma, o resultado esperado deste programa é mostrar no console do servidor de aplicação apenas as strings “Dois” e “Três”.

Repare que, se o array em questão for multidimensional — array de arrays — cada elemento do array principal é um outro array, que será passado como parâmetro para o Codeblock. Logo, vamos a um novo exemplo:

USER Function aTest17()
Local aCores := { {0,"Preto"}, {1,"Azul"} , {2,"Verde"} }
Local bBloco := {|x| conout("Cor "+cValToChar(x[1])+ " = " + x[2]  ) }

aEval(aCores,bBloco)

Return

Repare que cada elemento do array aCores é um array com duas posições, a primeira é o número da cor, e a segunda um texto (string) com o nome da cor. Com o programa acima, eu mostro no log de console todas as cores, com o número e a sua descrição. No Codeblock, eu recebo x ( posso dar o nome que eu quiser, dentro do codeblock os parâmetros são informados entre “|” ), e como eu sei que cada parâmetro recebido será uma array de duas posições, eu endereço os elementos deste array usando x[1] e x[2].

Se você não tivesse a função AEval(), para você fazer algo parecido, você teria que fazer algo assim:

For nPos := 1 to len(aArray) 
   Eval( bBloco, aArray[nPos] )
Next

Muitas vezes você precisa executar muito mais coisas do que apenas uma ou outra instrução para cada elemento do Array, algo mais complexo para colocar dentro de um Codeblock. Neste caso, é recomendável mesmo que você escreva o LOOP e as operações que você quer fazer no array. Tentar enfiar um caminhão de instruções dentro de um Codeblock acaba gerando aquele código macarrônico que nem quem escreveu consegue entender direito.

Função AFILL

Podemos usar a função AFILL para atribuir um conteúdo a vários elementos de um Array. A função AFILL não muda o tamanho do Array, apenas realiza um loop varrendo os elementos do array, e atribuindo a cada elemento o valor informado. Vamos a sintaxe da função:

AFILL ( <aArray> , <xValue> , [nInicio] , [nQuant] )
-- aArray é o Array a ter elementos atribuídos
-- xValue é o valor -- de qualquer tipo -- a ser atribuído a cada elemento 
-- nInicio ( default=1 ) indica a partir de qual elemento deve ser iniciada a atribuição 
-- nQuant ( default = todos ) indica quantas atribuições serão realizadas

Lembrando que, de forma similar ao AEVAL() , um AFILL simplesmente faz atribuição direta de xValue para cada elemento do array informado, baseado nos parâmetros. Logo, se você informar um array em xValue, cada elemento de aArray que receber a atribuição vai fazer uma REFERENCIA ao conteúdo do array informado em xValue — e não uma cópia ( ou CLONE). Um AFILL() seria algo parecido com:

For nPos := 1 to len(aArray)
   aArray[nPos] := xValue
Next

Função AINS

Usamos a função AINS para inserir um elemento nulo (NIL) em uma determinada posição do Array, deslocando todos os elementos a partir da posição informada para a frente. Como a função AINS não mexe no tamanho do array, o último elemento do array é descartado. Vamos a sintaxe e exemplo:

aIns (aArray , nPos)
-- aArray é o array a ser manipulado
-- nPos é a posição do elemento NIL a ser inserido

aDados := {1,3,4}
// Inserindo NIL na segunda posição 
aIns(aDados,2) 
// O array agora passou a ser { 1, NIL, 3 }
// Como eu não aumentei o tamanho do array
// antes de fazer a inserção, o último elemento 
// foi descartado

Imagine que eu esteja usando um Array simples, de tamanho N, com uma sequência ordenada de valores, e eu quero acrescentar um valor a mais nesta sequência, mantendo a ordenação.

// Quero inserir o valor 12, ele deve entrar na 3a posição do Array
aValores := { 5 , 10 , 15, 20 }

aadd(aValores,NIL) 
// Aumento o array acrescentando um elemento NIL no final do array
// O array agora passou a ser { 5 , 10  , 15 , 20 , NIL }

aIns(aValores,3) 
// Insiro um elemento NIL na terceira posição, deslocando todas as posições 
// posteriores um elemento para a frente, descartando o último 
// O array agora passou a ser {5 , 10 , NIL , 15 , 20 }

aValores[3] := 12
// Atribuo o valor na posição adequada. 
// O Array agora passou a ser { 5 , 10, 12 , 15, 20 }

 

Função ARRAY

A função Array() serve para retornar um array com um número determinado de elementos. Por default cada elemento não é inicializado — seu valor é NIL. Esta função também pode ser usada para retornar arrays multi-dimensionais. Vamos aos exemplos:

// Cria o array aTeste1 com 5 elementos 
aTeste1 := Array(5) 

// Cria o array aTeste2 com três elementos, onde cada 
// um dos elementos é um array de dois elementos
aTeste2 := Array(3,2)

// Cria o array aTeste3, com 8 elementos, cada um deles 
// é um array de 2 elementos, e cada um destes elementos 
// é um array com 3 elementos. São 48 elementos distintos 
// ( 8 x 2 x 3 ) em três dimensões. 
aTeste3 := Array(8,2,3)

O Array aTeste2 possui a mesma estrutura do Array aCores, porém os conteúdos dos elementos está NIL (Nulo em AdvPL). Caso seja necessário inicializar os elementos do array, com um tipo de dado diferente de “N” Numérico, basta informar o último parâmetro da chamada da função Array() com o valor desejado. Por exemplo, vamos inicializar um array de dimensão simples com 10 elementos, cada um deles com uma string de 20 espaços em branco.

aTeste3 := Array(10 , space(20) )

Poderíamos colocar no lugar da função space(20) uma string entre aspas com vinte espaços em branco, mas isso não tornaria nosso código facilmente legível. No caso de um array multi-dimensional criado pela função Array(), o valor de inicialização será usado para preencher os elementos do último nível. Por exemplo:

aTeste4 := Array(3,2,.F.)

A instrução acima vai criar um array de 3 elementos, onde dentro de cada um deles existe um array de dois elementos, onde cada elemento foi inicializado com o valor booleano Falso em AdvPL.

Observações

  • Qualquer valor numérico passado para a função Array() será interpretado como o número de elementos do array na dimensão informada.
  • Se o valor informado para a inicialização dos elementos for um array, todos os elementos serão inicializados com uma REFERÊNCIA a este Array, e não uma cópia.
  • Outra forma de colocar o mesmo conteúdo em múltiplos elementos de um Array de uma vez é usar a função AFILL().
  • Caso o array já exista e tenha conteúdo, podemos alocar mais elementos no final do Array usando a função AADD() especificando um valor NIL, a ser acrescentado no final do array, ou mesmo a função ASIZE(), que quando usada para aumentar um array, acrescenta ao final do array quantos elementos NIL forem necessários para o Array chegar  ao tamanho informado.

Função ASCAN

A exemplo das demais funções de manipulação de Arrays, a função ASCAN permite informar um Array e um valor de tipo simples a ser pesquisado. Internamente a função faz uma busca sequencial comparando cada elemento do array com o valor de busca informado. Podem ser informados valores do tipo “C” Caractere , “N” Numérico, “D” Data ou “L” lógico. Podemos especificar também um Codeblock no lugar do valor a ser pesquisado. Neste caso, a busca sequencial será feita executando o Codeblock informado como parâmetro para cada elemento do Array, até que o resultado seja verdadeiro, ou acabem os elementos do Array.

A busca sequencial em Array tende a ser progressivamente mais lenta quanto maior é o array, ou quanto mais execuções forem necessárias até que o elemento procurado seja localizado. O melhor caso é o valor pesquisado ser o primeiro elemento do array, e o pior caso é o valor pesquisado não existir — e para chegar a esta conclusão, o ASCAN varreu o array inteiro, comparando elemento por elemento.

Existem outros métodos de busca em Array no AdvPL, como por exemplo a criação de um Hash Map para os elementos de um array, que acelera absurdamente a busca por um valor — porém a busca não é feita pelo ASCAN, mas sim por uma função da classe de HASHMAP do AdvPL, onde a instância desse objeto objeto foi criado por um programa AdvPL a partir de um Array — Que não precisa estar previamente ordenado.

nPos := ASCAN( <aArray> , <xValue> , [nInicio] , [nQuant] )
-- nPos é o número da primeira posição do array onde o valor informado em xValue foi encontrado. 
-- xValue = Se for dos tipos C,D,N,L é um valor a ser procurado. Pode ser infromado também 
   um Codeblock que recebe um elemento do Array, e roda uma expressão de resultado booleano para 
   cada elemento do Array pesquisado. 
-- nInicio ( dafeult=1) indica a posição do elemento em aArray 
-- nQuant (default = todos ) indica quantos elementos serão pesquisados a partir do primeiro.

Quando usamos um array de arrays (multimensional) em AdvPL, somente conseguimos fazer uma busca com ASCAN() se for informado um Codeblock, que receberá em cada chamada um elemento do Array algo da busca, e deve tratar as colunas deste array para montar a condição de busca. Por exemplo:

USER Function aTest19()
Local aCores := { {0,"Preto"}, {1,"Azul"} , {2,"Verde"} }
Local nBusca, nPos
nBusca := 2
nPos := ascan( aCores , {|x| x[1] == nBusca })
If nPos > 0 
	MsgInfo("A cor pesquisada "+cValToChar(nBusca) + ;
	        " foi encontrada na posição "+ cValToChaR(nPos))
Else
	MsgStop("Cor nao encontrada.")
Endif
Return

No exemplo acima, o Codeblock utilizado compara a primeira coluna de cada elemento recebido durante a execução do ASCAN, retornando .T. (verdadeiro) assim que o primeiro elemento com a cor 2 (nBusca) for encontrada.

Função ASIZE

A função ASIZE permite alterar o tamanho (quantidade de elementos)  de um Array. Caso especificado um valor menor que o tamanho atual, os últimos elementos além do tamanho especificado são eliminados do Array. Caso o tamanho informado seja maior que o atual, novos elementos NIL serão acrescentados após o último elemento do Array.

ASIZE ( <aArray> , <nTamanho> )
-- aArray é o array a ser redimensionado 
-- nTamanho é a nova quantidade de elementos que este array deve ter

Ao aumentar o tamanho do array dinamicamente, a vantagem de se usar ASIZE ao invés de AADD é que precisamos apenas de uma chamada da função ASIZE para aumentar o array, sem mexer nos elementos já existentes. Por outro lado, embora a função AADD aumente em uma unidade o tamanho do Array, ela ao mesmo tempo aumenta o array e acrescenta um conteúdo novo ao final do Array, e quando usamos ASIZE devemos fazer uma atribuição para cada elemento.

A função ASIZE também é usada para limpar todos os elementos do Array da lista — ASIZE ( aArray , 0 ). Caso o conteúdo dos elementos não esteja sendo referenciado por outras variáveis, a memória utilizada é liberada. É interessante (e até recomendável) fazer isso para ajudar o gerenciador de memória do Protheus Server, quando o array não será mais utilizado. No caso de uma classe em AdvPL que tenha propriedades como Array, é interessante também limpá-los, dentro de um método criado para fazer o Clean-Up da instância antes de deletá-la.

Função ASORT

A função ASORT permite reordenar todos ou parte dos elementos de um array simples em ordem crescente de conteúdo, ou um array multidimensional usando uma expressão de ordenação customizada através de um Codeblock.

aSort( aArray , [nInicio] , [nQtant] , [bOrdBlock] )
-- aArray é o array a ter os elementos reordenados
-- nInicio (default=1) é a partir de qual posição ele será ordenado
-- nQuant (default=tudo) indica quantos elementos a partir do primeiro serão ordenados
-- bOrdBlock indica um cloco de código para customizar a ordenação

Vamos ao exemplo simples:

User Function aTest20()
Local aFrutas := {"Laranja","Banana","Pera","Maçã"}
Local nI

conout("Antes de ordenar")
For nI := 1 to len(aFrutas)
  conout("aFrutas ["+cValToChar(nI)+"] = "+aFrutas[nI])
Next

aSort(aFrutas) // Ordena o array em ordem crescente

conout("Depois de ordenar")
For nI := 1 to len(aFrutas)
  conout("aFrutas ["+cValToChar(nI)+"] = "+aFrutas[nI])
Next
Return

No exemplo acima, o resultado esperado é:

Antes de ordenar
aFrutas [1] = Laranja
aFrutas [2] = Banana
aFrutas [3] = Pera
aFrutas [4] = Maτπ
Depois de ordenar
aFrutas [1] = Banana
aFrutas [2] = Laranja
aFrutas [3] = Maτπ
aFrutas [4] = Pera

Agora, vamos a um exemplo usando um array multidimensional de 2 colunas.

USER Function aTest21()
Local aItens := { {"Régua" , 10.0 }, {"Caneta" , 2.0 } , {"Borracha", 3.0 } , {"Apagador" , 10.0 } }
Local nI

conout("Antes de ordenar")
For nI := 1 to len(aItens)
	conout("aItens ["+cValToChar(nI)+"] = { "+aItens[nI][1]+" , " + cValToChar(aItens[nI][2])+ "}")
Next

aSort(aItens,,, { |x,y| x[1] < y[1] })

conout("Depois de ordenar")
For nI := 1 to len(aItens)
	conout("aItens ["+cValToChar(nI)+"] = { "+aItens[nI][1]+" , " + cValToChar(aItens[nI][2])+ "}")
Next

Return

Agora, vamos entender o que faz o Codeblock que permite customizar a ordenação. O Codeblock vai receber dois parâmetros — que por exemplo eu chamei de x e y — , onde cada um deles pode ser qualquer elemento do Array a ser ordenado. O Codeblock deve retornar .T. (verdadeiro) se para efeito de ordenação o elemento x deve vir antes do elemento y, caso contrário deve retornar .F. (falso)

Como o array é multidimensional, cada elemento de aItens é um array de duas posições, então eu comparo a primeira coluna de x com a primeira coluna de y. Se x[1] < y[1], então estes elementos estão ordenados corretamente. A função ASORT internamente vai avaliar os elementos do Array quantas vezes forem necessárias para reordenar os elementos.

No caso de uma condição de sorteio composta, por exemplo eu quero ordenar um array de 3 colunas pela primeira e segunda colunas (chave composta), onde ambas as colunas sejam por exemplo do tipo “C” caractere, eu posso montar a seguinte expressão de ordenação do CodeBlock:

aSort( aItens ,,, {|x,y| x[1]+x[2] < y[1]+y[2] })

Existe uma segunda forma de montar esta expressão, um pouco menos intuitiva do que a primeira, porém sem a necessidade de concatenação dos campos, inclusive pode ser usada para colunas de tipos diferentes.

aSort(aItens ,,, { |x,y| ( x[1] < y[1] ) .OR. ; 
                 ( x[1] == y[1] .AND. x[2] < y[2] ) } )

Neste caso, eu considero que x está antes que y, caso a primeira coluna de x seja menor que a primeira coluna de y, OU , caso elas sejam iguais, mas a a segunda coluna de x seja menor que a segunda coluna de y. Veja o exemplo abaixo:

USER Function aTest22()
Local nI 
Local aItens := {}

aadd(aItens , { 1 , 3 } )
aadd(aItens , { 2 , 2 } )
aadd(aItens , { 3 , 1 } )
aadd(aItens , { 1 , 2 } )
aadd(aItens , { 3 , 3 } )
aadd(aItens , { 2 , 1 } )
aadd(aItens , { 3 , 2 } )
aadd(aItens , { 2 , 3 } )
aadd(aItens , { 1 , 1 } )

aSort(aItens ,,, { |x,y| ( x[1] < y[1] ) .OR. ;
                 ( x[1] == y[1] .AND. x[2] < y[2] ) } )

For nI := 1 to len(aItens)
	conout("N1 = " + cValToChar(aItens[nI][1]) + ;
               " N2 = " + cValToChar(aItens[nI][2]) )
Next

Return

O resultado obtido deve ser todos os elementos ordenados em ordem crescente da primeira coluna, e quando os elementos da primeira coluna forem iguais, a ordenação é feita considerando a segunda coluna.

N1 = 1 N2 = 1
N1 = 1 N2 = 2
N1 = 1 N2 = 3
N1 = 2 N2 = 1
N1 = 2 N2 = 2
N1 = 2 N2 = 3
N1 = 3 N2 = 1
N1 = 3 N2 = 2
N1 = 3 N2 = 3

Caso eu queira, por exemplo, ordem crescente pela primeira coluna, porém ordem decrescente pela segunda coluna, basta inverter a comparação feita com a segunda coluna, por exemplo:

aSort(aItens ,,, { |x,y| ( x[1] < y[1] ) .OR. ;
                         ( x[1] == y[1] .AND. x[2] > y[2] ) } )

Função ATAIL

A função ATAIL é uma forma de você acessar diretamente o último elemento do Array. Normalmente quando você quer acessar o último elemento de um Array, você usa:

xValue := aArray[len(aArray)]

Usando ATAIL(), você escreveria:

xValue := ATAIL(aArray)

Conclusão

Isso é praticamente tudo o que podemos fazer com um Array em AdvPL. Sim, ainda teremos mais um post sobre esse assunto, sobre aspectos gerais, tamanhos e dimensões, e inclusive boas práticas.

Espero que estas informações lhe sejam muito úteis, e desejo a todos TERABYTES DE SUCESSO !!! 😀

Referências

 

2 comentários sobre “Arrays em AdvPL – Parte 02

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s