Resposta rápida
- Primeiro índice
- 1, não 0.
aLista[0]gera erro de execução. - Tamanho
Len(aLista), sempre recalculado — o array cresce e encolhe em tempo de execução.- Remover
aDelseguido deaSize. SóaDeldeixa umNilno fim.- Procurar
aScandevolve a posição, ou0quando não acha.- Copiar
aClone. Atribuir com:=só cria outro nome para o mesmo array.
Nesta página8 seções
Array é o tipo que mais aparece em customização de Protheus: aHeader e aCols
de uma grade, o cabeçalho e os itens de um ExecAuto, o
PARAMIXB de um ponto de
entrada. Este artigo cobre as operações do dia a dia e as três armadilhas que
mais geram chamado. Os outros tipos estão em
tipos de dados em AdvPL.
Criar e crescer
Há duas formas de nascer: vazio, com {}, ou com tamanho fixo, com Array(n).
Nesse segundo caso, cada posição começa com Nil.
Local aFiliais := {} // vazio, Len() == 0
Local aMeses := Array(12) // 12 posições, todas Nil
Local nX := 0
aAdd(aFiliais, "0101") // Len() == 1
aAdd(aFiliais, "0102") // Len() == 2
For nX := 1 To Len(aFiliais)
ConOut("Filial " + cValToChar(nX) + ": " + aFiliais[nX])
Next nXUm array aceita elementos de tipos diferentes na mesma lista — caractere,
número, data, outro array. Isso é liberdade e também fonte de erro: nada
garante que aLista[3] é o que você imagina. O laço For e suas variações
estão em condições e laços.
Inserir no meio: aSize e depois aIns
aAdd só acrescenta no fim. Para abrir espaço numa posição específica, o
caminho é aumentar o array em uma posição e depois deslocar:
Local aFila := {"B", "C", "D"}
aSize(aFila, Len(aFila) + 1) // {"B","C","D",Nil}
aIns(aFila, 1) // {Nil,"B","C","D"}
aFila[1] := "A" // {"A","B","C","D"}aIns não aumenta nada: ele empurra os elementos para baixo e o último cai
fora. Sem o aSize antes, você perde o "D" sem nenhum aviso.
Remover: aDel não encolhe
É o espelho do caso anterior e a armadilha mais comum. aDel apaga a posição,
puxa os de baixo para cima e deixa Nil na última posição. O tamanho continua
o mesmo.
Local aFila := {"A", "B", "C", "D"}
aDel(aFila, 2) // {"A","C","D",Nil} -> Len() ainda é 4
aSize(aFila, Len(aFila) - 1) // {"A","C","D"} -> Len() agora é 3Esquecer o aSize não dá erro ali. Dá erro depois, num For qualquer que
concatena aFila[nX] com texto e encontra o Nil no fim. O aSize corta
sempre as últimas posições — funciona justamente porque o aDel já levou o
Nil para lá.
Atribuir aFila[2] := Nil também não remove: só esvazia o conteúdo.
Procurar: aScan com valor e com bloco
Em array simples, passe o valor procurado:
nPos := aScan(aFiliais, "0102") // 2, ou 0 se não existirEm array de arrays, o valor não serve — é preciso dizer onde comparar. Para
isso existe o bloco de código, que recebe cada elemento e devolve .T. quando
achou. O próprio padrão faz assim: GdFieldPos, a função que devolve a coluna
de um campo na grade e está declarada é um aScan com
bloco sobre o aHeader, comparando a segunda posição de cada linha com o
nome do campo.
Local aPrecos := {{"PA001", 12.50}, {"PA002", 8.90}, {"PA003", 30.00}}
Local nPos := 0
nPos := aScan(aPrecos, {|aItem| aItem[1] == "PA002"})
If nPos > 0
ConOut("Preço: " + cValToChar(aPrecos[nPos, 2]))
EndIfTeste o retorno sempre. aPrecos[0, 2] com nPos zerado é erro de
execução em produção.
Ordenar: aSort com bloco
aSort ordena o próprio array. Em array simples, basta passá-lo. Em array de
arrays, o bloco recebe dois elementos e devolve .T. quando o primeiro deve vir
antes:
// do maior preço para o menor
aSort(aPrecos,,, {|x, y| x[2] > y[2]})As duas vírgulas vazias são as posições inicial e a quantidade, que ficam no
padrão (o array inteiro). O mesmo formato aparece no próprio padrão, por
exemplo na função Ctb280Proc(), ordenando um aCols
pela primeira coluna.
Copiar: array é referência
Esta é a que mais custa, porque o código parece certo:
Local aOriginal := {"A", "B"}
Local aCopia := aOriginal // NÃO copia
aCopia[1] := "Z"
ConOut(aOriginal[1]) // "Z"A variável guarda uma referência ao array, não o conteúdo. aCopia := aOriginal cria um segundo nome para o mesmo array. O mesmo vale quando
você passa um array como parâmetro: a função chamada altera o array de quem
chamou. É exatamente o risco com aCols em ponto de entrada, discutido em
PARAMIXB.
Para ter uma cópia independente, inclusive dos subarrays:
aCopia := aClone(aOriginal)Multidimensional: o formato do ExecAuto
Array de arrays é como o Protheus representa tabela em memória. O aCols
tem uma linha por item e uma coluna por campo; o aHeader descreve essas
colunas. O ExecAuto usa outro formato: uma lista de trios campo, valor e
Nil, um por campo que você quer preencher. A função A036GrvNF() monta
o cabeçalho de um pedido de venda exatamente assim, campo a campo, sem
precisar descrever a estrutura inteira da tabela antes.
Local aCab := {}
Local aItens := {}
Local aLinha := {}
aAdd(aCab, {"C5_TIPO", "N", Nil})
aAdd(aCab, {"C5_CLIENTE", cCliente, Nil})
aAdd(aCab, {"C5_LOJACLI", cLoja, Nil})
aAdd(aCab, {"C5_CONDPAG", cCondPag, Nil})
aLinha := {}
aAdd(aLinha, {"C6_ITEM", "01", Nil})
aAdd(aLinha, {"C6_PRODUTO", cProduto, Nil})
aAdd(aLinha, {"C6_QTDVEN", 10, Nil})
aAdd(aLinha, {"C6_PRCVEN", 12.50, Nil})
aAdd(aItens, aLinha)Repare no aLinha := {} antes de cada item. Sem ele, e por causa da
referência, todos os itens de aItens apontariam para o mesmo array — e o
pedido sairia com a última linha repetida. Recriar o array a cada volta (ou
adicionar um aClone(aLinha)) resolve.
Para ler um valor desse formato, volta o aScan com bloco:
nPos := aScan(aCab, {|x| AllTrim(x[1]) == "C5_CLIENTE"})Quando o ExecAuto recusa o array, o caminho de diagnóstico está em MsExecAuto: como descobrir por que a rotina falhou, e a escolha entre ele e a gravação direta, em RecLock ou ExecAuto.
Em resumo
| Operação | Função | Cuidado |
|---|---|---|
| Criar | {} ou Array(n) | Array(n) começa com Nil |
| Acrescentar | aAdd | só no fim |
| Inserir no meio | aSize + aIns | sem aSize, perde o último |
| Remover | aDel + aSize | sem aSize, sobra Nil |
| Procurar | aScan | testar > 0 |
| Ordenar | aSort | bloco devolve .T. se x vem antes |
| Copiar | aClone | := não copia |
O próximo passo da série são os operadores, incluindo as comparações que você acabou de usar dentro dos blocos.
Referências
TDNtdn.totvs.com
TDN — AScanTDNtdn.totvs.com
TDN — ASortTDNtdn.totvs.com
TDN — ADelTDNtdn.totvs.com
TDN — AClone
Continue por aqui
- Vídeo
DesenvolvimentoTutorial
Escopo de variáveis em AdvPL: Local, Private, Static e Public
Local, Private, Static e Public em AdvPL: quem enxerga cada variável, quanto tempo ela vive e por que Local é o padrão.