1

I am trying to join two tables in Lua. Basically, I want to append the contents of one table to the end of the other. I've found lots of code to do this, but it doesn't work when the tables have other tables as values (subtables, I believe they are called?)

I found this question to be similar: Lua - merge tables?

And the code there worked, other than the content in the second table is overwriting the first table. But I do not want the entries in the first table to be overwritten. I just want all the content in the second table to be appended after the content in the first. But I can't figure out how to do that.

So when my first table looks similar to this:

   { Category = 
      { Answer = "String",
        Answer = "String"
      }
   },
   { Category = 
      { Answer = "String",
        Answer = "String"
      }
   }

And my second is the same, I want to end up with:

   { Category = 
      { Answer = "String",
        Answer = "String"
      }
   },
   { Category = 
      { Answer = "String",
        Answer = "String"
      }
   },
   { Category = 
      { Answer = "String",
        Answer = "String"
      }
   },
   { Category = 
      { Answer = "String",
        Answer = "String"
      }
   }

How can this be accomplished?

I am building these tables dynamically from XML files using this code: https://github.com/Cluain/Lua-Simple-XML-Parser

This is my XML file structure:

<?xml version="1.0" encoding="utf-8"?>
<library>
    <gametype1>
        <category name="">
            <answer></answer>
            <answer></answer>
            <answer></answer>
        </category>
        <category name="">
            <answer></answer>
            <answer></answer>
            <answer></answer>
        </category>
    </gametype1>

    <gametype2>
        <category name="">
            <answer></answer>
            <answer></answer>
            <answer></answer>
        </category>
        <category name="">
            <answer></answer>
            <answer></answer>
            <answer></answer>
        </category>
    </gametype2>
</library>

I then load the XML files like such:

gameAnswers1 = xml:loadFile( "file1.xml", system.ResourceDirectory )
gameAnswers2 = xml:loadFile( "file2.xml", system.ResourceDirectory )

gameAnswers1Gametype1 = {}
gameAnswers1Gametype1 = gameAnswers1.library.gametype1

gameAnswers1Gametype2 = {}
gameAnswers1Gametype2 = gameAnswers1.library.gametype2

gameAnswers2Gametype1 = {}
gameAnswers2Gametype1 = gameAnswers2.library.gametype1

gameAnswers2Gametype2 = {}
gameAnswers2Gametype2 = gameAnswers2.library.gametype2

What I would like to do now is concatenate the tables so that I have just one table of gametype1 data and one table of gametype2 data.

And so when I access the table of data by #gameAnswers1Gametype1.category I get the correct number of entries. With the current XML files I am using for testing, file1.xml has 9 category nodes in the gametype1 node and file2.xml has 1 category node in the gametype1 node. So I would expect that when I am done, I will have 10 category nodes in the concatenated table.

(If it matters, I am doing this with the Corona SDK.)

Any help would be most appreciated!

Community
  • 1
  • 1
thegdog
  • 13
  • 1
  • 4
  • 1
    `{ Category = { Answer = "String", Answer = "String" } }` is the same as `{ Category = { Answer = "String" } }` – Egor Skriptunoff Mar 16 '14 at 10:14
  • ``table_append = function (t1, t2) for i = 1, #t2 do t1[#t1 + 1] = t2[i] end end`` – Philipp Gesang Mar 16 '14 at 11:19
  • Egor, I was just using that XML example as a structural example. The "String" value would be different for each. – thegdog Mar 17 '14 at 21:53
  • Please show a prettyprint of the tables before your concatenation and how your result should be. You see there are never two table entries with the same key? Next step, correct your example above. – Deduplicator Mar 17 '14 at 22:37
  • I found some different XML parsing code that simplifies the data structure quite nicely. Let me try that and see if this changes anything. I will also be able to easily print the table, I believe. – thegdog Mar 18 '14 at 01:21
  • Everyone, thanks for your help. I was able to get @Schollii code working. – thegdog Mar 18 '14 at 01:41

2 Answers2

1

The structure you will receive from the XML lib for each of your two files is something like

{ 
    library = 
    {
        gametype1 =
        {
            category =
            {
                { -- category[1]
                    name="name1",
                    answer = {"ans1", "ans2", "ans3"}
                },
                { -- category[2]
                    name="name2",
                    answer = {"ans1", "ans2", "ans3"}
                },
            }
        },
        gametype2 =
        {
            category =
            {
                { -- category[1]
                    name="name1",
                    answer = {"ans1", "ans2", "ans3"}
                },
                { -- category[2]
                    name="name2",
                    answer = {"ans1", "ans2", "ans3"}
                },
            }
        },
    }
}

and you want to combine table1.library.gametype1 with table2.library.gametype1, and do the same for gametype2 of table1 and table2. Since you will not be changing the two tables after they are created, you don't need deep copy:

local function concat(fromTable, intoTable) 
    local lenFrom = #fromTable
    for i = 1, lenFrom do 
        table.insert(intoTable, fromTable[i])
    end 
end

local gameAnswers1 = xml:loadFile( "file1.xml", system.ResourceDirectory )
local gameAnswers2 = xml:loadFile( "file2.xml", system.ResourceDirectory )

local gameType1A = gameAnswers1.library.gametype1
local gameType1B = gameAnswers2.library.gametype1
concat(gameType1B.category, gameType1A.category)
-- now table contains its categories plus all those of table2
local tableGameType1 = gameType1A 

local gameType2A = gameAnswers1.library.gametype2
local gameType2B = gameAnswers2.library.gametype2
concat(gameType2B.category, gameType2A.category)
-- now table1 contains its categories plus all those of table2
local tableGameType2 = gameType2A 

Just beware that gameAnswers1 now contains all categories from both files.

Oliver
  • 27,510
  • 9
  • 72
  • 103
  • @thegdog Can you be more specific, like error you get, or problem, or have you resolved this using one of the other comments/answers? – Oliver Mar 17 '14 at 20:10
  • Thanks @Schollii, I also added more information about the table and data structure to my original post. I think the issue might be the sheer complexity of the tables that are being created by the XML parser. – thegdog Mar 17 '14 at 20:24
  • there is no sheer complexity here, all manageable, but tell me if you want to put t1 + t2 in a t3 (so t1 and t2 remain unchanged), or do you want to extend t1 with the contents of t2 (so t2 unchanged, but t1 changed, and there is no t3)? Also in your first comment you said "when I try to access the XML node in the table, I get a nil value": please show the code you used to do this. Then I will edit my answer accordingly. – Oliver Mar 17 '14 at 21:24
  • I am open to either creating a new table of combined data, or just appending the first table with data from the second table. I will not be editing/changing any of the data. It is simply meant to be read and displayed. In the code I added above, you can see what my "test" was. I have file1.xml with 9 **category** nodes in the **gametype1** node. And I have file2.xml with 1 **category** node in the **gametype1** node. Whatever table I create (or update) as part of this process, I expect to get a result of 10 when I do **#gameAnswers1Gametype1.category**. Does that make sense? – thegdog Mar 17 '14 at 21:51
  • Thanks again, @Schollii. I do appreciate your help, but that did not work either. I am confused about the **local tableGameType1 = table1** and **local tableGameType2 = table1** lines since I am not sure what **table1** is supposed to reference. But since you said that gameAnswers1 now has all the categories, I tried checking #gameAnswers1.library.gametype1.category and it comes back as 9, the original number. So the 1 from gameAnswers2 is not getting added to it, which I can confirm by dumping the table to the console and searching for one of the unique strings from file2.xml. – thegdog Mar 18 '14 at 00:47
  • Actually, I found the answer. Sort of. This does not work if there is only one **category** node in the second file. It does work if there are more than one **category** nodes. For whatever reason, the #fromTable is resolving at 0 if there is just 1 **category** node. If I add a second **category** node, it resolves as 2. Strange. I am also not using the **local tableGameType1 = table1** lines as they are not needed. Thanks @Schollii for your help! – thegdog Mar 18 '14 at 01:39
  • @thegdog sorry typo, fixed. – Oliver Mar 18 '14 at 04:58
0

Non-destructively concatenate multiple tables:

function table.concat(...)
  local r, n, t = {}, 1, {...}
  for i = 1, select('#', ...) do
    local t = t[i]
    if t then
      for i = 1, #t do
        r[n], n = t[i], n + 1
      end
    end
  end
end

This solution allows concatenating a table with itself, as well as ignoring additional hash-keys and omitted tables.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • Thanks @Deduplicator, but like with Schollii's examples, I get no errors, but when I try to access the data by number of entries, the number returned is the number that was in the first table. The content from second table is not there. – thegdog Mar 17 '14 at 20:14