Lua multiple assignment with tables
Asked Answered
A

4

8

This code:

function foo()
    return 1, 2, 3
end

bar = {}

bar = {a, b, c = foo()}

produces:

bar.a = nil
bar.b = nil
bar.c = 1

How can this be written so that you get:

bar.a = 1
bar.b = 2
bar.c = 3

without having to write something like this:

function foo()
    return 1, 2, 3
end

bar = {}
a, b, c = foo()

bar = {a = a, b = b, c = c}
Alexalexa answered 3/8, 2015 at 5:55 Comment(0)
P
7

BLUF

There's no straight forward or elegant way to do this. You'll have to do it manually like this

local r = { f() }           --> store all returned values in r
local bar = { }
local c = string.byte 'a'   --> start with 'a'
for _, v in ipairs(r) do
   local t = string.char(c)
   bar[t] = v               --> assign each value to respective letter
   c = c + 1
end

If you'd had a, b, c = foo() you'd get all the three values assigned to the three variables. However, you've

bar = { a, b, c = foo() }

This table constructor expression will get interpreted as the keys a, b, c getting inserted into the table, with only the last key having an associated value (aside: keys with no associated value are taken as nil; hence a and b never get inserted). Since there's only one variable to take the values returned by foo, except the first everything else it returns are discarded.

Alternatively bar = { foo() } will assign all values returned by foo as array values of bar. However, the key to access these would [1], [2], etc. and not 'a', 'b', etc.

Read below to know when the returned values get discarded and when they don't.


TL;DR All returned values are retained only when the function call is the last/only expression in a list of expressions; elsewhere all except the first are discarded.

Function call as a statement

In Lua, when we return multiple results from a function, all of them get discarded if the function call is a statement by itself.

foo()

will discard all three return values.

Function call in an expression

If it's used in an expression, only the first will be retained and everything else will be discarded.

x = foo() - 1
print(x)        -- prints 0; the values 2, 3 are discarded

Function call in an expression list

The entire list of values returned is retained only when the call appears as the last/only item in a list of expressions. Such list of expressions occur at four places in Lua:

  1. Multiple assignment

    E.g. local a, b, c, d = 0, f(). Here b, c, d get the values 1, 2, 3 respectively.

  2. Table constructor

    E.g. local t = { 0, f() }. All values returned by f are put into t following the first 0.

  3. Function call arguments

    E.g. g(a, f()). g would receive 4, not 2, arguments. a and the three values from f.

  4. return statement

    E.g. return 'a', f(). Additional to the string 'a', all values returned by f will be received at the calling end.

In all these situations, had f appeared not as the last expression in the list or wasn't the only expression, then all values it returned except the first would've been discarded.

Multiple assignment statement

In the multiple assignment statement, when the number of values assigned is lesser than number of variables, the extra variables be assigned to nil. When it's the other way around i.e if the number of variables are lesser, the extra values are discarded.

a, b, c = 1, 2         -- a = 1, b = 2, c = nil
a, b, c = 1, 2, 3, 4   -- 4 gets discarded
Provenance answered 3/8, 2015 at 8:1 Comment(2)
string.byte'a' will give you the ASCII value of 'a'--- no need for magic numbers.Brotherinlaw
Thanks for the tip! Fixed :)Provenance
T
7
bar = {}
bar.a, bar.b, bar.c = foo()
Thoroughfare answered 3/8, 2015 at 7:16 Comment(2)
So, there's no way to do structure (yes, it's a table) based multiple assigns other than assigning the members individually?Alexalexa
I'm not sure how it could be less individual than this. It's exactly what you tried, except in two lines. You can only bend the table constructor syntax so far.Thoroughfare
P
7

BLUF

There's no straight forward or elegant way to do this. You'll have to do it manually like this

local r = { f() }           --> store all returned values in r
local bar = { }
local c = string.byte 'a'   --> start with 'a'
for _, v in ipairs(r) do
   local t = string.char(c)
   bar[t] = v               --> assign each value to respective letter
   c = c + 1
end

If you'd had a, b, c = foo() you'd get all the three values assigned to the three variables. However, you've

bar = { a, b, c = foo() }

This table constructor expression will get interpreted as the keys a, b, c getting inserted into the table, with only the last key having an associated value (aside: keys with no associated value are taken as nil; hence a and b never get inserted). Since there's only one variable to take the values returned by foo, except the first everything else it returns are discarded.

Alternatively bar = { foo() } will assign all values returned by foo as array values of bar. However, the key to access these would [1], [2], etc. and not 'a', 'b', etc.

Read below to know when the returned values get discarded and when they don't.


TL;DR All returned values are retained only when the function call is the last/only expression in a list of expressions; elsewhere all except the first are discarded.

Function call as a statement

In Lua, when we return multiple results from a function, all of them get discarded if the function call is a statement by itself.

foo()

will discard all three return values.

Function call in an expression

If it's used in an expression, only the first will be retained and everything else will be discarded.

x = foo() - 1
print(x)        -- prints 0; the values 2, 3 are discarded

Function call in an expression list

The entire list of values returned is retained only when the call appears as the last/only item in a list of expressions. Such list of expressions occur at four places in Lua:

  1. Multiple assignment

    E.g. local a, b, c, d = 0, f(). Here b, c, d get the values 1, 2, 3 respectively.

  2. Table constructor

    E.g. local t = { 0, f() }. All values returned by f are put into t following the first 0.

  3. Function call arguments

    E.g. g(a, f()). g would receive 4, not 2, arguments. a and the three values from f.

  4. return statement

    E.g. return 'a', f(). Additional to the string 'a', all values returned by f will be received at the calling end.

In all these situations, had f appeared not as the last expression in the list or wasn't the only expression, then all values it returned except the first would've been discarded.

Multiple assignment statement

In the multiple assignment statement, when the number of values assigned is lesser than number of variables, the extra variables be assigned to nil. When it's the other way around i.e if the number of variables are lesser, the extra values are discarded.

a, b, c = 1, 2         -- a = 1, b = 2, c = nil
a, b, c = 1, 2, 3, 4   -- 4 gets discarded
Provenance answered 3/8, 2015 at 8:1 Comment(2)
string.byte'a' will give you the ASCII value of 'a'--- no need for magic numbers.Brotherinlaw
Thanks for the tip! Fixed :)Provenance
I
0
bar = {}
local abc = foo()
bar.a, bar.b, bar.c = abc, abc, abc

Simply bar.a, bar.b, bar.c = foo() will only set bar.a to foo(), the other two will be set to nil because they get set to the second and third values respectively, and you've only given one value.

Iodometry answered 9/2, 2019 at 12:25 Comment(0)
B
0

If you can, have foo() return a table formatted the right way.

function foo()
    return {a = 1, b = 2, c = 3}
end

bar = foo()
Bendy answered 10/2, 2020 at 3:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.