3

If i try to output this table, they are looped through in the false order:

local letters   =   {DIN1="hi", AIN1= "my", AIN2 ="name", DIN2="is"}

for name, value in pairs(letters) do
    print(name,value)
end

Expected Output:

DIN1   hi
AIN1   my
AIN2    name
DIN2   is

Output:

AIN1    my
DIN2    is
DIN1    hi
AIN2    name

How can i code it so that the for loop runs through the tables actual order? (The order how it was defined)

Edit: I don't need the alphabetic order, but the same order as in the definition of the table.

Edit: I need to have the key AND the value printed. In the answer "Lua in pairs with same order as it's written" there will be only indexnumber and value printed

Black
  • 18,150
  • 39
  • 158
  • 271
  • 2
    Checkout http://stackoverflow.com/questions/30970034/lua-in-pairs-with-same-order-as-its-written – moteus Jun 22 '15 at 08:31
  • I checked it out, and it does not fit to my problem. I need to keep both, the key and the value. – Black Jun 22 '15 at 08:42
  • 1
    @EdwardBlack It does fit perfectly, as it stores all of index, key and value. – user3125367 Jun 22 '15 at 10:43
  • 2
    The Lua-users wiki also [has a page on this](http://lua-users.org/wiki/SortedIteration). – ComicSansMS Jun 22 '15 at 10:48
  • @ComicSansMS this should be an answer – Bartek Banachewicz Jun 22 '15 at 10:50
  • @ComicSansMS, this is working on the example which i provided, but not on a table like this: t = { ['DIN1'] = 'xxx', ['DIN2'] = 'xxx', ['AIN1'] = 'xxx', } I don't need the alphabetic order, i need the exact table order – Black Jun 22 '15 at 11:06
  • 2
    @EdwardBlack Define _exact table order_ then. – ComicSansMS Jun 22 '15 at 11:11
  • @ComicSansMS the same order as in the definition – Black Jun 22 '15 at 13:41
  • Lua-users wiki entries that do this [OrderedTable](http://lua-users.org/wiki/OrderedTable) and [Ordered Associative Table](http://lua-users.org/wiki/OrderedAssociativeTable) and [SO Question](http://stackoverflow.com/questions/30970034/lua-in-pairs-with-same-order-as-its-written) – ryanpattison Jun 22 '15 at 14:12
  • Simply stated, you are confusing a table constructor expression with a table value by assuming that a characteristic of a table constructor expression in source code (syntactical field order) carries over to a table value at runtime. – Tom Blodget Jun 22 '15 at 17:11
  • @rpattiso there is not even a code example on how to use the function "Ordered" in your first link... – Black Jun 23 '15 at 07:12
  • @EdwardBlack the first implementation in that link is presumably used as `t = Ordered(); t.anykey = "anyValue"; for k, v in pairs(t) do print(k, v) end` etc. The *other* implementation in that link includes an example. – ryanpattison Jun 23 '15 at 13:55

4 Answers4

2

You may utilize integer part of the table to store keys in order:

function add(t, k, v, ...)
    if k ~= nil then
        t[k] = v
        t[#t+1] = k
        return add(t, ...)
    end
    return t
end

t = add({ }, "A", "hi", "B", "my", "C", "name", "D", "is")

for i,k in ipairs(t) do
    local v = t[k]
    print(k, v)
end

Of course, this assumes that integer keys are not used by anything except add.

insert(t, k, v) and remove(t, k) left as an exercise to the reader.

EDIT: Ellipsis (dots) in add function allow passing as many arguments as needed to set many kv-pairs at once. Without that, we would be only able to set one pair per call, like add(t, "A", "hi"). Function definition add(t, k, v, ...) assigns first three arguments to t, k, v and leaves others untouched. Then add processes first pair (t[k]=v) and recurses with the rest ... of arguments.

          t   k    v    ...
level 1: (t, "A", "hi", "B", "my", "C", "name", "D", "is")
level 2: (t,         <- "B", "my", "C", "name", "D", "is")
level 3: (t,                    <- "C", "name", "D", "is")
level 4: (t,                                 <- "D", "is")
level 5: (t,                                          <- )

At level 5, k and v take nils, because argument list is too short, and recursion stops.

user3125367
  • 2,920
  • 1
  • 17
  • 17
  • Thx for your answer, but when i run this code, nothing happens. – Black Jun 22 '15 at 10:54
  • Apologies, there was an obvious bug in the add function's first line. Fixed that. Oh, messed it again. There is tested version now. – user3125367 Jun 22 '15 at 10:56
  • 1
    An important implication of this solution is that you are no longer able to index the table by the original keys. That is, a `t.A = "hey"` will no longer work. This is the typical tradeoff for tables in Lua, if you need order, you have to index with numerical indices. – ComicSansMS Jun 22 '15 at 11:24
  • 1
    Another note, `t.A = nil` will keep the key in the array part and `nil` will appear during iterating through the table, instead of removing the entry. – ryanpattison Jun 22 '15 at 12:57
  • @ComicSansMS `t.A = "hey"` – we can't also definitely tell *what* should happen then. And we have no control if we implement that behavior as a black box. Look: you need the order, you enforce it using ordered construction (e.g. arg.list), transparent helper code stores it somewhere, you deal with implications on demand. – user3125367 Jun 22 '15 at 13:43
  • @ComicSansMS Btw "tradeoff" is not so typical in my xp – at least C+GLib, Perl, Python(afaik), Objective-C store no order for generic hash table keys. If you want order for anything, you build [and maintain] an index, following reasonable semantics. – user3125367 Jun 22 '15 at 13:49
  • I just don't understand the "..." in the code, why are these dots needed for? I never saw this before. – Black Jun 22 '15 at 13:55
  • 1
    @EdwardBlack Added description – user3125367 Jun 22 '15 at 14:05
2

The Lua-users wiki has a page that addresses this particular problem.

Quoting the code from that page:

--[[
Ordered table iterator, allow to iterate on the natural order of the keys of a
table.

Example:
]]

function __genOrderedIndex( t )
    local orderedIndex = {}
    for key in pairs(t) do
        table.insert( orderedIndex, key )
    end
    table.sort( orderedIndex )
    return orderedIndex
end

function orderedNext(t, state)
    -- Equivalent of the next function, but returns the keys in the alphabetic
    -- order. We use a temporary ordered key table that is stored in the
    -- table being iterated.

    key = nil
    --print("orderedNext: state = "..tostring(state) )
    if state == nil then
        -- the first time, generate the index
        t.__orderedIndex = __genOrderedIndex( t )
        key = t.__orderedIndex[1]
    else
        -- fetch the next value
        for i = 1,table.getn(t.__orderedIndex) do
            if t.__orderedIndex[i] == state then
                key = t.__orderedIndex[i+1]
            end
        end
    end

    if key then
        return key, t[key]
    end

    -- no more value to return, cleanup
    t.__orderedIndex = nil
    return
end

function orderedPairs(t)
    -- Equivalent of the pairs() function on tables. Allows to iterate
    -- in order
    return orderedNext, t, nil
end

Not that natural order of the keys here means that you start with the smallest key (as if determined by a < b style comparisons), which is not necessarily the key that you write first in your code when filling the table (determining the latter is way more complicated and way less reasonable).

You can then simply use the orderedPairs function as a drop-in replacement for pairs:

local letters   =   {A="hi", B= "my", C ="name", D="is"}

for name, value in orderedPairs(letters) do
    print(name,value)
end
ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • Thank you for your answer, but as i said i need the exact table order. If i have this table: t = {D1="hi", A2="my"} then i need this output: D1 -> hi; A2 -> my – Black Jun 22 '15 at 11:13
  • 1
    I personally think that Lua users just don't get it. Lua is simple and efficient and allows you to write just a few-line function *to do that*, but folks still overcomplicate everything, always trying to build a *framework*. – user3125367 Jun 22 '15 at 11:15
  • 1
    @EdwardBlack You realize that even your own answer does not achieve this? This requires adding a metatable that keeps track of newly added entries and exposes that info to an iterator. Needless to say, it's very hard to come up with reasonable semantics for such a function. I get the impression you might be victim of an [XY-problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) here. What exactly are you trying to achieve with this? Are you sure that a key/value table is the right tool here? If order of elements is that important, an array seems a reasonable choice. – ComicSansMS Jun 22 '15 at 11:17
  • @ComicSansMS if you are talking about the first solution, then you are correct, but the second one which i still have to figure out how to code, should work perfectly. But i also voted for your solution, because it is the solution for the example – Black Jun 22 '15 at 11:22
  • @ComicSansMS I just edited my code in my own solution, i removed the non working code and added my new solution which works. – Black Jun 22 '15 at 11:35
2

For the record, there is no exact order.

A table in Lua is a set of key-value pairs. A table definition is just shorthand for setting several pairs at once. The order the pairs are defined only matters when there are repeated keys: the last pair with the same key is the one that remains in the table.

lhf
  • 70,581
  • 9
  • 108
  • 149
0

I was able to work around it with this code, but this solution is not perfect of course.

local letters       =   {"DIN1=hi", "DIN2 = my", "AIN1 = name", "LE = is" }
local pattern       = "(%S+)%s*=%s*(%S+)"

for _, n in pairs(letters) do

    key, value = n:match(pattern)

    print(key, value)
end

Output:

DIN1    hi
DIN2    my
AIN1    name
LE      is
Black
  • 18,150
  • 39
  • 158
  • 271
  • 4
    This is grossly overcomplicated. – Bartek Banachewicz Jun 22 '15 at 10:41
  • My answer should be above. Besides, whether I would or wouldn't show another solution, this one is still overcomplicated. – Bartek Banachewicz Jun 22 '15 at 10:48
  • but it works, and it was the only workaround that i could figure out... why do you downvote a working solution? – Black Jun 22 '15 at 10:52
  • 1
    Because I don't think it's a good way to solve that problem. Good code isn't about just getting it to work in whatever way. – Bartek Banachewicz Jun 22 '15 at 10:53
  • 2
    The pattern: `"(%S+)%s*=%s*(%S+)"` is probably better if you want to ignore the spaces around the equals. – ryanpattison Jun 22 '15 at 13:30
  • Thx @rpattiso! With this pattern i don't need the if condition anymore. I updated the code. – Black Jun 22 '15 at 13:48
  • while it doesn't seem to be your use case, this method is very limited in what can be stored and how it can be accessed. tables cannot be stored as keys or values, strings and numbers are indistinguishable and lead to precision losses, spaces and `=` are not allowed in keys or values, and normal lookups `t.key` will not work and will require a linear search through the table. – ryanpattison Jun 22 '15 at 14:06
  • yes it is not in my use case fortunatelly, it would be still possible to insert new keys and values. You just have to write table.insert(mytable, myindex, myvalue). And myvalue contains your key and value, e.g. "mykey=value" – Black Jun 22 '15 at 14:17