Como verificar se uma tabela contém um elemento em Lua?

97

Existe um método para verificar se uma tabela contém um valor? Tenho minha própria função (ingênua), mas gostaria de saber se existe algo "oficial" para isso? Ou algo mais eficiente ...

function table.contains(table, element)
  for _, value in pairs(table) do
    if value == element then
      return true
    end
  end
  return false
end

A propósito, o principal motivo pelo qual estou usando essas funções é usar tabelas como conjuntos, ou seja, sem elementos duplicados. Existe algo mais que eu possa usar?

Wookai
fonte
3
o que significa a notação _ ,?
Martin
24
É simplesmente uma variável "lixo" chamada _. pairs()retorna key, value, mas neste exemplo eu só preciso do valor. É uma espécie de convenção (adotada no livro "Programação em Lua" lua.org/pil/index.html ) usar esta _variável para armazenar coisas que você não precisa.
Wookai
Eu vi a convenção de nomear variáveis ​​"lixo" _usadas em Python e JavaScript também.
iono

Respostas:

115

Você pode colocar os valores como as chaves da tabela. Por exemplo:

function addToSet(set, key)
    set[key] = true
end

function removeFromSet(set, key)
    set[key] = nil
end

function setContains(set, key)
    return set[key] ~= nil
end

Há um exemplo mais completo aqui .

interjay
fonte
13
Um usuário anônimo propôs a seguinte correção para o seu código: Se o valor no conjunto com a chave especificada for FALSE, a função setContains () retornará um falso, embora haja um item na tabela com a chave especificada. a linha "return set [key] ~ = nil" corrige esse erro.
ofertas de
Talvez tambémfunction keysOfSet(set) local ret={} for k,_ in pairs(set) do ret[#ret+1]=k end return ret end
Jesse Chisholm
24

Dada a sua representação, sua função é tão eficiente quanto pode ser feita. É claro que, conforme observado por outros (e praticado em linguagens anteriores a Lua), a solução para seu problema real é mudar a representação. Quando você tem tabelas e deseja conjuntos, transforma as tabelas em conjuntos usando o elemento set como chave e truecomo valor. +1 para interjay.

Norman Ramsey
fonte
2

Não consigo pensar em outra maneira de comparar valores, mas se você usar o elemento do conjunto como a chave, poderá definir o valor para qualquer coisa diferente de nulo. Assim, você obtém pesquisas rápidas sem ter que pesquisar toda a tabela.

Joel
fonte
2

Eu sei que este é um post antigo, mas queria acrescentar algo para a posteridade. A maneira simples de lidar com o problema que você tem é fazer outra tabela, de valor para chave.

ie. você tem 2 tabelas com o mesmo valor, uma apontando para uma direção e uma apontando para a outra.

function addValue(key, value)
    if (value == nil) then
        removeKey(key)
        return
    end
    _primaryTable[key] = value
    _secodaryTable[value] = key
end

function removeKey(key)
    local value = _primaryTable[key]
    if (value == nil) then
        return
    end
    _primaryTable[key] = nil
    _secondaryTable[value] = nil
end

function getValue(key)
    return _primaryTable[key]
end

function containsValue(value)
    return _secondaryTable[value] ~= nil
end

Você pode então consultar a nova tabela para ver se ela contém o 'elemento' chave. Isso evita a necessidade de iterar por todos os valores da outra tabela.

Se descobrir que você não pode realmente usar o 'elemento' como uma chave, porque não é uma string, por exemplo, adicione uma soma de verificação ou tostringnela, por exemplo, e use-a como a chave.

Por que você quer fazer isso? Se suas tabelas forem muito grandes, a quantidade de tempo para iterar em cada elemento será significativa, impedindo que você faça isso com muita frequência. A sobrecarga de memória adicional será relativamente pequena, pois armazenará 2 ponteiros para o mesmo objeto, em vez de 2 cópias do mesmo objeto. Se suas tabelas forem muito pequenas, isso importará muito menos; na verdade, pode ser até mais rápido iterar do que ter outra pesquisa de mapa.

A formulação da pergunta, entretanto, sugere fortemente que você tem um grande número de itens para tratar.

James
fonte
Uma boa explicação, mas não acrescenta nada à discussão. Provavelmente teria sido uma ideia melhor editar a resposta de interjay.
bcdan
1
Além disso, '.key' deve ser substituído por '[chave]' em todos os lugares neste código (o mesmo que 'valor')
Njol