55

ORIGINAL POST

Given that there is no built in function in Lua, I am in search of a function that allows me to append tables together. I have googled quite a bit and have tried every solutions I stumbled across but none seem to work properly.

The scenario goes like this: I am using Lua embeded in an application. An internal command of the application returns a list of values in the form of a table.

What I am trying to do is call that command recursively in a loop and append the returned values, again in the form of a table, to the table from previous iterations.


EDIT

For those who come across this post in the future, please note what @gimf posted. Since Tables in Lua are as much like arrays than anything else (even in a list context), there is no real correct way to append one table to another. The closest concept is merging of tables. Please see the post, "Lua - merge tables?" for help in that regard.

Community
  • 1
  • 1
John Mark Mitchell
  • 4,522
  • 4
  • 28
  • 29
  • Possible dupe: http://stackoverflow.com/questions/1283388/lua-merge-tables. You mention "recursivly in a loop". Do you search for a deep-copy + merge? – gimpf Sep 11 '09 at 14:00
  • The following are the links I found that offered solutions: http://ardoris.wordpress.com/2008/08/10/lua-merge-two-tables-awesome3-rc2-config/ http://www.idevgames.com/forum/archive/index.php/t-10223.html Though I understand the approach of each, neither seem to work. Do you have a working solution? – John Mark Mitchell Sep 11 '09 at 15:09
  • gimpf, maybe I am not being completely clear. Merging tables and concatinating tables are similar but very different. I am interested in appending one table to another, thus the use of the word concatenate. – John Mark Mitchell Sep 11 '09 at 15:16
  • Please see my edit; an example of what you want to do in the form of 3 lua tables (2 in, 1 out) would be very helpful. – gimpf Sep 11 '09 at 18:19

15 Answers15

39

Overcomplicated answers much?

Here is my implementation:

function TableConcat(t1,t2)
    for i=1,#t2 do
        t1[#t1+1] = t2[i]
    end
    return t1
end
Community
  • 1
  • 1
Weeve Ferrelaine
  • 683
  • 1
  • 7
  • 12
  • 1
    wouldn't `ipairs` iteration with `table.insert` be better (more readable and/or faster)? – Nas Banov Mar 20 '15 at 23:29
  • 1
    ipairs is barely more costly than normal, the reason not to use it is it doesn't guarantee order of items in the table. the reason not to use insert is it's dramatically more costly than a standard index set on a table, because insert will call a routine that pushes values in the table back from the index it was called on, while there are no values past [#t+1], the routine is still called, causing a performance issue, on a compiled language, there is no difference, but using an interpreted language, we have to be careful what all we ask the computer to do for us – Weeve Ferrelaine Mar 22 '15 at 17:44
  • 9
    From what i know, `ipairs` guarantees iteration order be `for i=1` ... till the first t[i]==nil, no? Which for non-degenerate cases is same as `for i=1,#t`. Re `insert` vs indexing set though, you are right - i measured and there is 5-6x performance difference – Nas Banov Mar 22 '15 at 18:41
  • I'm pretty sure I read somewhere that pairs doesn't guarantee order, but you may be right, although it's most likely a safe assumption that it will be ordered, whether guaranteed or not- The code is correct, it should not be [#t1+i], because #t1 will increase as the table size goes up, leaving a sparse table (almost always a bad thing in Lua) – Weeve Ferrelaine Mar 30 '15 at 04:44
  • 9
    NB: `ipairs` ~= `pairs`. There is a reason for the "i". `pairs` is disorderly, `ipairs` isn't – Nas Banov Jun 28 '15 at 17:49
  • 1
    @GermanK no bug. `t1` is the table being inserted into, thus `#t1+1` is the length of `t1` plus 1 (i.e. where you should be inserting the next item). – thomas May 12 '16 at 18:33
  • @thomas Right! Should I delete my comment to avoid confusion? – GermanK May 14 '16 at 23:05
  • @GermanK might be an idea to update your comment to show its not a bug. If you can't then I'm not sure. – thomas May 15 '16 at 21:33
  • @thomas Yeah, I couldn't, so I deleted it. Yet. this discussion remains as archeological evidence for its previous existence :) Thanks for catching that! – GermanK May 16 '16 at 15:36
  • 5
    Shouldn't the third line be `t1[#t1+i] = t2[i]`? – Dave Yarwood Mar 10 '17 at 12:30
  • The fact that this both mutates the first argument AND returns it is a little confusing, although I guess the OP didn't say what interface they wanted. – detly Sep 09 '19 at 23:23
  • 1
    the third line should be 't1[#t1+1] = t2[i]', otherwise in the 2nd iteration #t1 is 2 and i is 2 and the index 3 is skipped. I'll edit the answer – G. B. Dec 17 '20 at 14:24
  • Does not work for `t1={a={1},2}` `t2={3,d={4}}` – rboy May 03 '23 at 02:05
26

If you want to concatenate an existing table to a new one, this is the most concise way to do it:

local t = {3, 4, 5}
local concatenation = {1, 2, table.unpack(t)}

Although I'm not sure how good this is performance-wise.

Romário
  • 1,664
  • 1
  • 20
  • 30
  • 14
    Just note that this only work if `table.unpack` is the last argument (unlike Python's `*t` or JavaScript's `...t`), see https://stackoverflow.com/questions/37372182/what-is-happening-when-i-call-unpack-as-luas-function-arguments – user202729 Nov 15 '21 at 07:13
15

And one more way:

for _,v in ipairs(t2) do 
    table.insert(t1, v)
end

It seems to me the most readable one - it iterates over the 2nd table and appends its values to the 1st one, end of story. Curious how it fares in speed to the explicit indexing [] above

Nas Banov
  • 28,347
  • 6
  • 48
  • 67
8

A simple way to do what you want:

local t1 = {1, 2, 3, 4, 5}
local t2 = {6, 7, 8, 9, 10}

local t3 = {unpack(t1)}
for I = 1,#t2 do
    t3[#t1+I] = t2[I]
end
warspyking
  • 3,045
  • 4
  • 20
  • 37
  • 2
    why the `{unpack(t1)}` ?! all it does i make copy of `t1` but question implied updating in-place? – Nas Banov Mar 20 '15 at 23:22
  • 2
    @NasBanov He asked how to concatenate. When you concatenate strings you get a new string. I assumed he wanted something like that. – warspyking Dec 07 '15 at 11:34
  • 1
    hmm, you are right regarding the word "concatenation" in the title. But the question speaks about "appending", which is a mutator. Still `{ unpack(tbl) }` is a neat trick for cloning a table - with limitations (something like 1M elements) – Nas Banov Dec 08 '15 at 22:21
  • @NasBanov However this contradiction in terms is the questioner's fault, so I see accepting either answer as valid and correct is fine. – warspyking Dec 08 '15 at 22:23
5

To add two tables together do this

    ii=0
for i=#firsttable, #secondtable+#firsttable do
    ii=ii+1
    firsttable[i]=secondtable[ii]
end

use the first table as the variable you wanted to add as code adds the second one on to the end of the first table in order.

  • i is the start number of the table or list.
  • #secondtable+#firsttable is what to end at.

It starts at the end of the first table you want to add to, and ends at the end of the second table in a for loop so it works with any size table or list.

Adam Eberlin
  • 14,005
  • 5
  • 37
  • 49
Red_M
  • 75
  • 1
  • 2
  • This is wrong. You have to start with i=(#firsttable+1), or you will munch over the last element in the first table. In case of the first table being empty you will even try to access firsttable[0], but arrays are indexed starting with 1 in lua. – scravy Aug 21 '13 at 17:16
4

In general the notion of concatenating arbitrary tables does not make sense in Lua because a single key can only have one value.

There are special cases in which concatenation does make sense. One such is for tables containing simple arrays, which might be the natural result of a function intended to return a list of results.

In that case, you can write:

-- return a new array containing the concatenation of all of its 
-- parameters. Scaler parameters are included in place, and array 
-- parameters have their values shallow-copied to the final array.
-- Note that userdata and function values are treated as scalar.
function array_concat(...) 
    local t = {}
    for n = 1,select("#",...) do
        local arg = select(n,...)
        if type(arg)=="table" then
            for _,v in ipairs(arg) do
                t[#t+1] = v
            end
        else
            t[#t+1] = arg
        end
    end
    return t
end

This is a shallow copy, and makes no attempt to find out if a userdata or function value is a container or object of some kind that might need different treatment.

An alternative implementation might modify the first argument rather than creating a new table. This would save the cost of copying, and make array_concat different from the .. operator on strings.

Edit: As observed in a comment by Joseph Kingry, I failed to properly extract the actual value of each argument from .... I also failed to return the merged table from the function at all. That's what I get for coding in the answer box and not testing the code at all.

IAbstract
  • 19,551
  • 15
  • 98
  • 146
RBerteig
  • 41,948
  • 7
  • 88
  • 128
  • +1 on the notion for "natural result of a function ... return a list of results". This is quite probable. – gimpf Sep 12 '09 at 07:44
  • I think there is an error in this function, I think you need another `select` in there after the `for` to get the actual value out of `...`. http://lua-users.org/wiki/VarargTheSecondClassCitizen See Issue 8 – Joseph Kingry Mar 03 '10 at 17:30
  • 1
    Yup. Apparently I didn't test this code before posting, or that defect would have been obvious. More obvious in hindsight is the missing `return t` before the last `end`. – RBerteig Mar 04 '10 at 23:01
2

If you want to merge two tables, but need a deep copy of the result table, for whatever reason, use the merge from another SO question on merging tables plus some deep copy code from lua-users.

(edit Well, maybe you can edit your question to provide a minimal example... If you mean that a table

 { a = 1, b = 2 }

concatenated with another table

{ a = 5, b = 10 }

should result in

{ a = 1, b = 2, a = 5, b = 10 }

then you're out of luck. Keys are unique.

It seems you want to have a list of pairs, like { { a, 1 }, { b, 2 }, { a, 5 }, { b, 10 } }. You could also use a final structure like { a = { 1, 5 }, b = { 2, 10 } }, depending on your application.

But the simple of notion of "concatenating" tables does not make sense with Lua tables. )

Community
  • 1
  • 1
gimpf
  • 4,503
  • 1
  • 27
  • 40
  • gimf, you were right. I was misinterpreting the use of lists in Tables to think that they could simply be concatenated. Further testing led me to the conclusion that what I really needed to be doing was a merge. Thank you for your help and patience with a Lua newbie. – John Mark Mitchell Sep 12 '09 at 03:17
  • 1
    @John, we were all newbies once... coming from complex languages, it is sometimes surprising how much power is hiding inside Lua's simplicity. It can take a while to grok it. – RBerteig Sep 12 '09 at 18:06
2

Here is an implementation I've done similar to RBerteig's above, but using the hidden parameter arg which is available when a function receives a variable number of arguments. Personally, I think this is more readable vs the select syntax.

function array_concat(...)
    local t = {}

    for i = 1, arg.n do
        local array = arg[i]
        if (type(array) == "table") then
            for j = 1, #array do
                t[#t+1] = array[j]
            end
        else
            t[#t+1] = array
        end
    end

    return t
end
Neil Sainsbury
  • 1,420
  • 3
  • 12
  • 17
1

Here is my implementation to concatenate a set of pure-integer-indexing tables, FYI.

  1. define a function to concatenate two tables, concat_2tables
  2. another recursive function concatenateTables: split the table list by unpack, and call concat_2tables to concatenate table1 and restTableList

    t1 = {1, 2, 3}
    t2 = {4, 5}
    t3 = {6}
    
    concat_2tables = function(table1, table2)
        len = table.getn(table1)
        for key, val in pairs(table2)do
            table1[key+len] = val
        end
        return table1
    end
    
    concatenateTables = function( tableList )
        if tableList==nil then
            return  nil
        elseif table.getn(tableList) == 1 then
            return  tableList[1]
        else
            table1 = tableList[1]
            restTableList = {unpack(tableList, 2)}
            return concat_2tables(table1, concatenateTables(restTableList))
        end
    end
    
    tt = {t1, t2, t3}
    t = concatenateTables(tt)  
    
ouxiaogu
  • 151
  • 1
  • 11
1

EDIT


Here's a better solution, the other one tended to overwrite numeric keys, the usage is still the same:

function merge(...)
  local temp = {}
  local index = 1
  local result = {}
  
  math.randomseed(os.time())

  for i, tbl in ipairs({ ... }) do
    for k, v in pairs(tbl) do
      if type(k) == 'number' then
        -- randomize numeric keys
        k = math.random() * i * k
      end
      
      temp[k] = v
    end
  end
  
  for k, v in pairs(temp) do
    if type(k) == "number" then
      -- Sort numeric keys into order
      if result[index] then
        index = index + 1
      end
      
      k = index
    end
    
    result[k] = v
  end

  return result
end

ORIGINAL


A wee bit late to the game, but this seems to work for me:

function concat(...)
  local result = {}
  
  for i, tbl in ipairs({...}) do
    for k, v in pairs(tbl) do
      if type(k) ~= "number" then
        result[k] = v
      else
        result[i] = v
      end
    end
  end
  
  return result
end

It might be a bit overcomplicated, but it takes an infinite amount of arguments, and works for both key-value pairs and regular "arrays" (numbers as keys). Here's an example

kaptcha
  • 68
  • 2
  • 10
  • This is the only solution that worked for tables with key/indexes `t1={a={a=1}, b=2, 5}` `t2={c={c=3}, d=4, 6}` – rboy May 03 '23 at 02:57
  • However note that it doesn't maintain the order of the elements in the table when merging them together. – rboy May 03 '23 at 03:06
0
-- Lua 5.1+
function TableAppend(t1, t2)
    -- A numeric for loop is faster than pairs, but it only gets the sequential part of t2
    for i = 1, #t2 do
        t1[#t1 + 1] = t2[i] -- this is slightly faster than table.insert
    end

    -- This loop gets the non-sequential part (e.g. ['a'] = 1), if it exists
    local k, v = next(t2, #t2 ~= 0 and #t2 or nil)
    while k do
        t1[k] = v -- if index k already exists in t1 then it will be overwritten
        k, v = next(t2, k)
    end
end
Finn
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 14 '22 at 21:10
0

I like the simplicity in @Weeve Ferrelaine answer, but mutations may cause many issues and in general, are not desirable.

Version with NO MUTATION.
---@param t1 {}
---@param t2 {}
function TableConcat(t1,t2)
    local tOut = {}

    for i = 1, #t1 do
        tOut[i] = t1[i]
    end

    for i = #t1, #t1 + #t2 do
        tOut[i] = t2[i]
    end

    return tOut
end
Original implementation, that's mutating t1.
function TableConcat(t1,t2)
    for i=1,#t2 do
        t1[#t1+1] = t2[i]
    end
    return t1
end
undg
  • 787
  • 4
  • 14
0

The other solutions here suffered from 3 issues:

  1. Did not work with tables that contain key/values
  2. Didn't maintain the order of the elements in the tables while merging
  3. Did not work with tables with mixed numeric indices and key based indices

This solution is a variant of the original solution proposed by @kaptcha which addresses the shortcomings noted above:

--- Function to merge/join tables
--- @param ... table List of tables to be merged
--- @return table Merged table
function MergeTables(...)
    local result = {}

    for i, tbl in ipairs({...}) do
      for k, v in pairs(tbl) do
        if type(k) ~= "number" then
          result[k] = v
        else
          table.insert(result, v)
        end
      end
    end

    return result
end

Usage:

local t1 = { a={1}, b={b=2} }
local t2 = { c={3}, d={d=4} }
local tMerged = MergeTables(t1, t2)
rboy
  • 2,018
  • 1
  • 23
  • 35
0

use table.insert() function

table1 = {
  "param1=value1",
  "param2=value2",
  "param3=value3"
}
table2 = {
  "param21=value1",
  "param23=value2",
  "param23=value3"
}
table.insert(table1, table.concat(table2, ","))
print(table.unpack(table1));
-1

Use table.concat:

http://lua-users.org/wiki/TableLibraryTutorial

> = table.concat({ 1, 2, "three", 4, "five" })
12three4five
> = table.concat({ 1, 2, "three", 4, "five" }, ", ")
1, 2, three, 4, five
> = table.concat({ 1, 2, "three", 4, "five" }, ", ", 2)
2, three, 4, five
> = table.concat({ 1, 2, "three", 4, "five" }, ", ", 2, 4)
2, three, 4
darkfrei
  • 122
  • 5