Como obter o número de entradas em uma tabela Lua?


132

Parece uma pergunta "deixe-me pesquisar no google por você", mas de alguma forma não consigo encontrar uma resposta. O #operador Lua conta apenas entradas com chaves inteiras e o mesmo table.getn:

tbl = {}
tbl["test"] = 47
tbl[1] = 48
print(#tbl, table.getn(tbl))   -- prints "1     1"

count = 0
for _ in pairs(tbl) do count = count + 1 end
print(count)            -- prints "2"

Como obtenho o número de todas as entradas sem contá-las?


3
@ lhf: Eu escrevi um serializador que lembra todos os objetos que viu, e da próxima vez que vê, emite uma referência inteira em vez do objeto. A maneira natural de escrever isso é algo como dictionary[value] = #dictionary + 1, onde #representa o número de todos os objetos. O que me pergunto é por que você não quer isso: em todos os casos de uso sãos para # (veja a resposta em kaizer.se), a contagem de todos os objetos é exatamente igual ao que # já retorna; parece que fazer # contar tudo é estritamente uma melhoria. Claro que sou novato em Lua e pode estar perdendo o objetivo.
Roman Starkov

32
@lhf: Não é legal da sua parte questionar a competência do programador, perguntando por que ele precisa fazer algo para o qual todas as linguagens de programação razoáveis ​​tenham uma função simples.
Timwi

5
@ Timwi: Não é legal da sua parte dizer a um dos autores da linguagem Lua que Lua não está entre as linguagens de programação "razoáveis". ;-) Aliás, eu nunca precisei dessa informação também.
Alexander Gladysh

5
Acho que nunca usei todos os recursos de um único idioma. Isso não significa que eles não são úteis para os outros :)
Roman Starkov

7
@sylvanaar Na minha opinião, o #operador está mal definido. Isso é tão facilmente corrigível: primeiro, faça #determinístico e, em segundo lugar, introduza um novo operador ou função para obter a contagem danada. Fim da história ... Por que eles têm que ser tão teimosos? :)
Roman Starkov

Respostas:


129

Você já tem a solução na pergunta - a única maneira é iterar a tabela inteira pairs(..).

function tablelength(T)
  local count = 0
  for _ in pairs(T) do count = count + 1 end
  return count
end

Além disso, observe que a definição do operador "#" é um pouco mais complicada do que isso. Deixe-me ilustrar isso, tomando esta tabela:

t = {1,2,3}
t[5] = 1
t[9] = 1

De acordo com o manual, qualquer um de 3, 5 e 9 são resultados válidos para #t. A única maneira sensata de usá-lo é com matrizes de uma parte contígua sem valores nulos.


42
Ainda tremo com a memória da minha experiência com Lua, quando percebi pela primeira vez que o valor de retorno de um operador básico como esse #não é determinístico.
Roman Starkov

5
Oh, é provavelmente determinístico. É exatamente a mesma coisa que quando o padrão C deixa algo como comportamento definido pela implementação. A razão pela qual é assim é que diferentes implementadores podem escolher diferentes opções de implementação.
Nakedible

19
According to the manual, any of 3, 5 and 9 are valid results for #t. De acordo com o manual, chamar # em não-seqüências é indefinido . Isso significa que qualquer resultado (-1, 3, 3,14, 5, 9) é válido.
Cubuspl42

6
Em relação aos resultados válidos: u0b34a0f6ae está correto para Lua 5.1, enquanto cubuspl42 está correto para Lua 5.2. Em ambos os casos, a coisa toda é completamente louca.
21715 Jeremy

9
O fato de # em uma não sequência não gerar uma exceção é apenas uma das coisas que tornam o uso da lua um pouco como se cortar para se sentir melhor.
boatcoder

21

Você pode configurar uma meta-tabela para rastrear o número de entradas; isso pode ser mais rápido que a iteração se essas informações forem necessárias com frequência.


Existe uma maneira conveniente de lidar com a exclusão de entradas com esse método?
U0b34a0f6ae

Infelizmente, parece que a função __newindex não dispara em atribuições nulas, a menos que o índice não exista, então parece que você teria que canalizar a remoção de entrada por meio de uma função especial.
ergosys

1
Você deve armazenar dados em uma tabela separada (por exemplo, acessível como valor superior para __index e __newindex). Em seguida, __index e __newindex seriam acionados para cada acesso à tabela. Você deve verificar se o desempenho é aceitável.
Alexander Gladysh

@ Alexander: Ah sim, e então o próximo ponto de tropeço: se você proxy da tabela, a iteração normal por pares não funciona. Isso será possível resolver na Lua 5.2, ouvi dizer.
precisa saber é o seguinte

Haveria metamétodos de __pairs e __ipairs na versão 5.2 ... Se você quiser fazer isso na versão 5.1, terá que substituir a função de pares () pela sua. Mas isso é provavelmente demais. :-)
Alexander Gladysh

3

Há uma maneira, mas pode ser decepcionante: use uma variável adicional (ou um dos campos da tabela) para armazenar a contagem e aumente sempre que fizer uma inserção.

count = 0
tbl = {}

tbl["test"] = 47
count = count + 1

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

Não há outra maneira, o operador # só funcionará em tabelas do tipo matriz com chaves consecutivas.


3
Isso pode ser automatizado com uma mesa de proxy e metamétodos, como mencionado por resposta das ergosys
RBerteig

Tive a impressão, pelos comentários, de que a coisa proxytable / metamethods ainda não suporta totalmente esse cenário, então aceitarei isso como a melhor maneira atualmente disponível.
Roman Starkov

A contagem é a única maneira de tabelas, e adicionar linhas ao criar as tabelas é melhor do que uma função para contá-las sempre que você precisar da contagem. Você pode adicionar uma chave no final com o valor definido para a contagem.
Henrik Erlandsson

2

A maneira mais fácil que eu conheço para obter o número de entradas em uma tabela é com '#'. #tableName obtém o número de entradas, desde que numeradas:

tbl={
    [1]
    [2]
    [3]
    [4]
    [5]
}
print(#tbl)--prints the highest number in the table: 5

Infelizmente, se eles não estiverem numerados, não funcionará.


2

Você poderia usar a biblioteca de penlight . Isso tem uma função sizeque fornece o tamanho real da tabela.

Ele implementou muitas das funções que podemos precisar enquanto estiver programando e faltando em Lua.

Aqui está a amostra para usá-lo.

> tablex = require "pl.tablex"
> a = {}
> a[2] = 2
> a[3] = 3 
> a['blah'] = 24

> #a
0

> tablex.size(a)
3

1
local function CountedTable(x)
    assert(type(x) == 'table', 'bad parameter #1: must be table')

    local new_t = {}
    local mt = {}

    -- `all` will represent the number of both
    local all = 0
    for k, v in pairs(x) do
        all = all + 1
    end

    mt.__newindex = function(t, k, v)
        if v == nil then
            if rawget(x, k) ~= nil then
                all = all - 1
            end
        else
            if rawget(x, k) == nil then
                all = all + 1
            end
        end

        rawset(x, k, v)
    end

    mt.__index = function(t, k)
        if k == 'totalCount' then return all
        else return rawget(x, k) end
    end

    return setmetatable(new_t, mt)
end

local bar = CountedTable { x = 23, y = 43, z = 334, [true] = true }

assert(bar.totalCount == 4)
assert(bar.x == 23)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = 24
bar.x = 25
assert(bar.x == 25)
assert(bar.totalCount == 4)

1
Ao postar uma resposta, é recomendável publicar a quantidade mínima de código que responde diretamente a uma pergunta e explicar como o código responde à pergunta. Veja aqui .
cst1992

__newindexchamar somente quando uma nova chave for definida; portanto, não haverá chance de chamar __newindexquando definirmos niluma chave existente.
Frank AK

-1

parece que quando os elementos da tabela são adicionados pelo método insert, getn retornará corretamente. Caso contrário, temos que contar todos os elementos

mytable = {}
element1 = {version = 1.1}
element2 = {version = 1.2}
table.insert(mytable, element1)
table.insert(mytable, element2)
print(table.getn(mytable))

Ele imprimirá 2 corretamente

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.