English 中文(简体)
How to remove a lua table entry by its key?
原标题:

I have a lua table that I use as a hashmap, ie with string keys :

local map = { foo = 1, bar = 2 }

I would like to "pop" an element of this table identified by its key. There is a table.remove() method, but it only takes the index of the element to remove (ie a number) and not a generic key. I would like to be able to do table.remove(map, foo ) and here is how I implemented it :

function table.removekey(table, key)
    local element = table[key]
    table[key] = nil
    return element
end

Is there a better way to do that ?

最佳回答

No, setting the key s value to nil is the accepted way of removing an item in the hashmap portion of a table. What you re doing is standard. However, I d recommend not overriding table.remove() - for the array portion of a table, the default table.remove() functionality includes renumbering the indices, which your override would not do. If you do want to add your function to the table function set, then I d probably name it something like table.removekey() or some such.

问题回答

TLDR

(because you re only damn trying to remove a thing from a map, how hard can that be)

Setting a key to nil (e.g. t[k] = nil) is not only accepted and standard, but it s the only way of removing the entry from the table s "content" in general. And that makes sense. Also, array portion and hashmap portion are implementation details and shouldn t have ever be mentioned in this Q/A.

Understanding Lua s tables

(and why you can t remove from an infinite)

Lua tables don t literally have concept of "removing an entry from a table" and it hardly has a concept of "inserting an entry to a table". This is different from many other data structures from different programming languages.

In Lua, tables are modelling a perfect associative structure of infinite size.

Tables in Lua are inherently mutable and there s only one fundamental way to construct a new table: using the {} expression. Constructing a table with initial content (e.g. t = {10, 20, ["foo"] = 123, 30} or anything alike) is actually a syntactic sugar equivalent to first constructing a new table (e.g. t = {}} and then setting the "initial" entries one by one (e.g. t[1] = 10, t[2] = 20, t["foo"] = 123, t[3] = 30) . The details of how the de-sugaring works doesn t help with understanding the discussed matter, so I will be avoiding the table construction sugar in this answer.

In Lua, a freshly-constructed table initially associates all possible values with nil. That means that for a table t = {}, t[2] will evaluate to nil, t[100] will evaluate to nil, t["foo"] will evaluate to nil, t[{}] will evaluate to nil, etc.

After construction, you can mutate the table by setting a value at some key. Then that key will be now associated with that value. For example, after setting t["foo"] = "bar", the key "foo" will now be associated with the value "bar". In consequence, t["foo"] will now evaluate to "bar".

Setting nil at some key will associate that key to nil. For example, after setting t["foo"] = nil, "foo" will (again) be associated with nil. In consequence, t["foo"] will (again) evaluate to nil.

While any key can be associated to nil (and initially all possible keys are), such entries (key/value pairs) are not considered a part of the table (i.e. aren t considered part of the table content).

Functions pairs and ipairs (and multiple others) operate on table s content, i.e. the of associations in which the value isn t nil. The number of such associations is always finite.

Having everything said above in mind, associating a key with nil will probably do everything you could expect when saying "removing an entry from a table", because t[k] will evaluate to nil (like it did after constructing the table) and functions like pairs and ipairs will ignore such entries, as entries (associations) with value nil aren t considered "a part of the table".

Sequences

(if tables weren t already tricky)

In this answer, I m talking about tables in general, i.e. without any assumption about their keys. In Lua, tables with a particular set of keys can be called a sequence, and you can use table.remove to remove an integer key from such table. But, first, this function is effectively undefined for non-sequences (i.e. tables in general) and, second, there s no reason to assume that it s more than a util, i.e. something that could be directly implemented in Lua using primitive operations.

Which tables are or aren t a sequence is another hairy topic and I won t get into details here.

Referencing the reference

(I really didn t make up all that)

Everything said above is based on the official language reference manual. The interesting parts are mostly chapter 2.1 – Values and Types...

Tables can be heterogeneous; that is, they can contain values of all types (except nil). Any key with value nil is not considered part of the table. Conversely, any key that is not part of a table has an associated value nil.

This part is not worded perfectly. First, I find the phrase "tables can be heterogeneous" confusing. It s the only use of this term in the reference and the "can be" part makes it non-obvious whether "being heterogeneous" is a possible property of a table, or whether it tables are defined that way. The second sentence make the first explanation more reasonable, because if "any key with value nil is not considered part of the table", then it means that "a key with value nil" is not a contradiction. Also, the specification of the rawset function, which (indirectly) gives semantics to the t[k] = v syntax, in the 6.1 – Basic Functions chapter says...

Sets the real value of table[index] to value, without invoking any metamethod. table must be a table, index any value different from nil and NaN, and value any Lua value.

As nil values are not special-cased here, that means that you can set t[k] to nil. The only way to understand that is to accept that from now on, that key will be "a key with value nil", and in consequence "will not be considered part of the table" (pairs will ignore it, etc.), and as "any key that is not part of a table has an associated value nil", t[k] will evaluate to nil.

The whole reference also doesn t mention "removing" a key or an entry from tables in any other place.

Another perspective on tables

(if you hate infinities)

While I personally find the perspective from the reference elegant, I also understand that the fact that it s different from other popular models might make it more difficult to reason about.

I believe that the following perspective is effectively equivalent to the previous one.

You can think that...

  • {} returns an empty table.
  • t[k] evaluates to v if t contains key k, and nil otherwise
  • Setting t[k] = v inserts a new entry (k, v) to the table if it doesn t contain key k, updates such entry if t already contains key k, and finally, as a special case, removes the entry for the key k if v is nil
  • The content of the table (i.e. what s considered "a part of the table") is the set of all entries from the table

In this model, tables aren t capable of "containing" nil values.

This is not how the language reference defines things, but to the best of my understanding, such model is observably equivalent.

Don t talk implementation details

(unless you re sure that that s what you mean)

The so-called "hashmap portion" of the table (which supplements the so-called "array portion" of the table) are implementation details and talking about them, unless we discuss performance or the explanation of specific undefined or implementation-defined behaviors, is in my opinion confusing in the best case and plain wrong in the worst.

For example, in a table constructed like this... t = {}, t[1] = 10, t[2] = 20, t[3] = 30, the array portion will (probably!) be [10, 20, 30] and setting t[2] = nil will "remove" the entry (2, 20) "from the array part", possibly also resizing it or moving 3 -> 30 to the hashmap part. I m not really sure. I m just saying this to prove that discussing implementation details is not what we want to do here.





相关问题
How to remove a lua table entry by its key?

I have a lua table that I use as a hashmap, ie with string keys : local map = { foo = 1, bar = 2 } I would like to "pop" an element of this table identified by its key. There is a table.remove() ...

Detect if any key is pressed in C# (not A, B, but any)

[EDIT 3] I kind of "solved it" by at using the "strange" version. At least for the most important keys. It is suffient for my case, where I want to check that ALT and ALT+A are not ...

How to get key value

My sample JSON string is as below: {"8776337":{"text":"Test Message","status":"d","created_time":"1244475327","reply_number":"447624800500","completed_time":"1244475373","credits_cost":"0.4"}} "...

Hibernate map collection with constant key

I m trying to map a collection (of type map) using a foreign key and a fixed value as the key/mapping arguments. I have several tables of product types and a language table which holds stuff like ...

Changing the value in a map in Groovy

This is about a very basic program I m writing in Groovy. I have defined a map inside a method: def addItem() { print("Enter the item name: ") def itemName = reader.readLine() print(...

SWT Cross-Platform Enter Detection

I have a global filter (Display.addFilter) in SWT in which I want to detect Enter key. In Windows, pressing Enter generates SWT.CR in keyCode part of KeyListener event. Is this assumption safe for ...

热门标签