Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate over values in an object in Lua (by order)

Tags:

lua

I am looking for a simple solution for my problem. I've got this code:

local Month = {
    Jan = 1, Feb = 2, Mar = 3, Apr = 4,
    May = 5, Jun = 6, Jul = 7, Aug = 8,
    Sep = 9, Oct = 10, Nov = 11, Dec = 12
}
...
p.main = function()
    local text = ""
    for k,v in pairs(Month) do
        text = text .. " " .. k;
    end
    return text;
end

When I call p.main() I would like it to print Jan Feb ... Dec (in order) though it prints Jan Mar Apr Oct Feb Dec Nov Aug Jul May Jun Sep which seems to me quite a random order.

Thanks in advance!

like image 466
Niles Avatar asked Oct 15 '25 08:10

Niles


1 Answers

The hash part of tables in Lua is unordered. String keys go in the hash part. This is why you're observing the "random" order.

Simply convert your Month table from a mapping from month names to numbers to a mapping from numbers to names, i.e. flip it and convert it to a list:

local monthnames = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}

then you can conveniently iterate it in order using ipairs (rather than pairs) - even more, your p.main function simplifies to a single call to table.concat, a Lua built-in function for concatenating a list of strings, and since the value is constant, you can pre-calculate it:

local space_separated_monthnames = table.concat(monthnames, " ")
function p.main()
    return space_separated_monthnames
end

This won't add a leading space, but I don't think that's intended anyways.

If you need to keep the old lookup, no problem - you can generate it from the list:

local monthnumber = {}
for i, name in ipairs(monthnames) do
    monthnumber[name] = i
end

Also note that string concatenation in a loop is an antipattern in Lua since it is both inefficient (quadratic vs. linear, very noticeable if you're building longer strings at least) and inconvenient to write (e.g. your code emits a probably unwanted leading space and more code would be required to not emit it). In general, if you see something like:

local str = vals[1]
for i = 2, #vals do
    str = str .. separator .. vals[i]
end

refactor it to

local str = table.concat(vals, separator)

both for performance and readability.

like image 91
LMD Avatar answered Oct 17 '25 05:10

LMD