184

I'm having trouble displaying the contents of a table which contains nested tables (n-deep). I'd like to just dump it to std out or the console via a print statement or something quick and dirty but I can't figure out how. I'm looking for the rough equivalent that I'd get when printing an NSDictionary using gdb.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Cliff
  • 10,586
  • 7
  • 61
  • 102

20 Answers20

196

If the requirement is "quick and dirty"

I've found this one useful. Because of the recursion it can print nested tables too. It doesn't give the prettiest formatting in the output but for such a simple function it's hard to beat for debugging.

function dump(o)
   if type(o) == 'table' then
      local s = '{ '
      for k,v in pairs(o) do
         if type(k) ~= 'number' then k = '"'..k..'"' end
         s = s .. '['..k..'] = ' .. dump(v) .. ','
      end
      return s .. '} '
   else
      return tostring(o)
   end
end

e.g.

local people = {
   {
      name = "Fred",
      address = "16 Long Street",
      phone = "123456"
   },

   {
      name = "Wilma",
      address = "16 Long Street",
      phone = "123456"
   },

   {
      name = "Barney",
      address = "17 Long Street",
      phone = "123457"
   }

}

print("People:", dump(people))

Produces the following output:

People: { [1] = { ["address"] = 16 Long Street,["phone"] = 123456,["name"] = Fred,} ,[2] = { ["address"] = 16 Long Street,["phone"] = 123456,["name"] = Wilma,} ,[3] = { ["address"] = 17 Long Street,["phone"] = 123457,["name"] = Barney,} ,}

hookenz
  • 36,432
  • 45
  • 177
  • 286
  • 18
    Well done for sharing something that doesn't need an external library. – Julian Knight Feb 10 '20 at 20:52
  • On really big table your function throws stackoverflow error – IC_ Jun 07 '20 at 13:40
  • 1
    I didn't write this. I found it somewhere and modified it to produce a string that I could print. Would love to credit the original author. – hookenz Mar 11 '21 at 02:46
  • @Herrgot - that would have to be a ridiculously big table and lots of nesting. You can't beat this for simplicity. It suits most quick debug use cases. – hookenz Mar 11 '21 at 02:53
  • 1
    It probably wasn't a very nested table but a table with a self reference in it somewhere, looping indefinitely, it will hit a stackoverflow. – MoonLite Aug 29 '21 at 16:16
  • 1
    For convenient debugging outside my main code i added `elseif type(o) == "string" then return tostring("\"" .. o .. "\"")`. Now i can use the output to test with a hardcoded table. – Konrni Apr 13 '22 at 09:03
  • initialize with `local s = ''` and replace the return line into `return '{ ' .. string.sub(s, 1, -3) .. ' } '` removes the trailing comma – Valen Aug 11 '22 at 07:35
  • This code breaks for boolean fields in tables, such as `{[true]=1}`. Fixable with `if type(k) == 'boolean' then k = tostring(k)` before the number type check and converting the number type check to an `elseif` branch in the same conditional statement. – t-mart Oct 25 '22 at 21:35
153

I know this question has already been marked as answered, but let me plug my own library here. It's called inspect.lua, and you can find it here:

https://github.com/kikito/inspect.lua

It's just a single file that you can require from any other file. It returns a function that transforms any Lua value into a human-readable string:

local inspect = require('inspect')

print(inspect({1,2,3})) -- {1, 2, 3}
print(inspect({a=1,b=2})
-- {
--   a = 1
--   b = 2
-- }

It indents subtables properly, and handles "recursive tables" (tables that contain references to themselves) correctly, so it doesn't get into infinite loops. It sorts values in a sensible way. It also prints metatable information.

Regards!

kikito
  • 51,734
  • 32
  • 149
  • 189
  • 1
    Maybe you should add your library to the [Lua Wiki](http://lua-users.org/wiki/TableSerialization). I see your library also prints metatables, which the other libraries do not. – Michal Kottman Feb 07 '12 at 09:12
  • The thing is that inspect.lua doesn't really fit into the "serialization" category. The text it returns isn't valid Lua code; it's supposed to be used for debugging / human read. I suppose I could add a small link at the end or something. – kikito Feb 07 '12 at 10:09
  • 2
    Added inspect.lua to the wiki. – kikito Feb 07 '12 at 11:10
  • Please put this on luarocks – Hack-R Sep 03 '17 at 18:58
  • 8
    @Hack-R it is on luarocks: `luarocks install inspect` – kikito Sep 04 '17 at 10:15
  • Is it just me or does this not work for nested tables? The other version below by Matt works for nested. – John Lee Jun 11 '19 at 15:50
  • @JohnLee It works with nested tables. Here are the passing specs: https://github.com/kikito/inspect.lua/blob/master/spec/inspect_spec.lua#L86 https://github.com/kikito/inspect.lua/blob/master/spec/inspect_spec.lua#L148 – kikito Jun 14 '19 at 09:58
  • 2
    @JohnLee note however that in recent versions of the library it will try very hard to not repeat the same table more than once. Instead it compresses them to `` if they appear more than once https://github.com/kikito/inspect.lua/blob/master/spec/inspect_spec.lua#L171
    – kikito Jun 14 '19 at 09:59
  • 1
    Nicely done, very useful. – N Altun Apr 20 '22 at 16:40
74

Feel free to browse the Lua Wiki on table serialization. It lists several ways on how to dump a table to the console.

You just have to choose which one suits you best. There are many ways to do it, but I usually end up using the one from Penlight:

> t = { a = { b = { c = "Hello world!", 1 }, 2, d = { 3 } } }
> require 'pl.pretty'.dump(t)
{
  a = {
    d = {
      3
    },
    b = {
      c = "Hello world!",
      1
    },
    2
  }
}
999PingGG
  • 54
  • 1
  • 5
Michal Kottman
  • 16,375
  • 3
  • 47
  • 62
  • 9
    Dumb and even more newbie question: how do I go about installing an extension like pl.pretty? It's be nice if I could just do something like a gem install without futzing with unrolling tar balls and finding the ideal spot on my HD to situate things. Is there a quick/painless "do-it-this-way"? – Cliff Feb 06 '12 at 22:53
  • 1
    Dah, I should have looked at the home page prior to posting that last comment! Installation is not as quick/painless as I'd hoped but not too bad. – Cliff Feb 06 '12 at 22:55
  • penlight shines the light on what I was looking for! – Cliff Feb 06 '12 at 23:35
  • 7
    @Cliff luarocks to install penlight – zztczcx Oct 15 '13 at 05:04
  • `luarocks install penlight` – HappyFace Oct 22 '20 at 21:15
  • penlight explicitly states that this is _not_ a serialization function. https://lunarmodules.github.io/Penlight/libraries/pl.pretty.html – jaiks Jan 11 '22 at 07:51
  • @jaiks that's true and should be noted. That said, the original question asks for something to "display content", "dump it to stdout" and "quick and dirty", which penlight (and other answers in this question) fulfill. – Michal Kottman Jan 12 '22 at 18:25
31

found this:

-- Print contents of `tbl`, with indentation.
-- `indent` sets the initial level of indentation.
function tprint (tbl, indent)
  if not indent then indent = 0 end
  for k, v in pairs(tbl) do
    formatting = string.rep("  ", indent) .. k .. ": "
    if type(v) == "table" then
      print(formatting)
      tprint(v, indent+1)
    elseif type(v) == 'boolean' then
      print(formatting .. tostring(v))      
    else
      print(formatting .. v)
    end
  end
end

from here https://gist.github.com/ripter/4270799

works pretty good for me...

JCH2k
  • 3,361
  • 32
  • 25
25

Most pure lua print table functions I've seen have a problem with deep recursion and tend to cause a stack overflow when going too deep. This print table function that I've written does not have this problem. It should also be capable of handling really large tables due to the way it handles concatenation. In my personal usage of this function, it outputted 63k lines to file in about a second.

The output also keeps lua syntax and the script can easily be modified for simple persistent storage by writing the output to file if modified to allow only number, boolean, string and table data types to be formatted.

function print_table(node)
    local cache, stack, output = {},{},{}
    local depth = 1
    local output_str = "{\n"

    while true do
        local size = 0
        for k,v in pairs(node) do
            size = size + 1
        end

        local cur_index = 1
        for k,v in pairs(node) do
            if (cache[node] == nil) or (cur_index >= cache[node]) then

                if (string.find(output_str,"}",output_str:len())) then
                    output_str = output_str .. ",\n"
                elseif not (string.find(output_str,"\n",output_str:len())) then
                    output_str = output_str .. "\n"
                end

                -- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
                table.insert(output,output_str)
                output_str = ""

                local key
                if (type(k) == "number" or type(k) == "boolean") then
                    key = "["..tostring(k).."]"
                else
                    key = "['"..tostring(k).."']"
                end

                if (type(v) == "number" or type(v) == "boolean") then
                    output_str = output_str .. string.rep('\t',depth) .. key .. " = "..tostring(v)
                elseif (type(v) == "table") then
                    output_str = output_str .. string.rep('\t',depth) .. key .. " = {\n"
                    table.insert(stack,node)
                    table.insert(stack,v)
                    cache[node] = cur_index+1
                    break
                else
                    output_str = output_str .. string.rep('\t',depth) .. key .. " = '"..tostring(v).."'"
                end

                if (cur_index == size) then
                    output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}"
                else
                    output_str = output_str .. ","
                end
            else
                -- close the table
                if (cur_index == size) then
                    output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}"
                end
            end

            cur_index = cur_index + 1
        end

        if (size == 0) then
            output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}"
        end

        if (#stack > 0) then
            node = stack[#stack]
            stack[#stack] = nil
            depth = cache[node] == nil and depth + 1 or depth - 1
        else
            break
        end
    end

    -- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
    table.insert(output,output_str)
    output_str = table.concat(output)

    print(output_str)
end

Here is an example:

local t = {
    ["abe"] = {1,2,3,4,5},
    "string1",
    50,
    ["depth1"] = { ["depth2"] = { ["depth3"] = { ["depth4"] = { ["depth5"] = { ["depth6"] = { ["depth7"]= { ["depth8"] = { ["depth9"] = { ["depth10"] = {1000}, 900}, 800},700},600},500}, 400 }, 300}, 200}, 100},
    ["ted"] = {true,false,"some text"},
    "string2",
    [function() return end] = function() return end,
    75
}

print_table(t)

Output:

{
    [1] = 'string1',
    [2] = 50,
    [3] = 'string2',
    [4] = 75,
    ['abe'] = {
        [1] = 1,
        [2] = 2,
        [3] = 3,
        [4] = 4,
        [5] = 5
    },
    ['function: 06472B70'] = 'function: 06472A98',
    ['depth1'] = {
        [1] = 100,
        ['depth2'] = {
            [1] = 200,
            ['depth3'] = {
                [1] = 300,
                ['depth4'] = {
                    [1] = 400,
                    ['depth5'] = {
                        [1] = 500,
                        ['depth6'] = {
                            [1] = 600,
                            ['depth7'] = {
                                [1] = 700,
                                ['depth8'] = {
                                    [1] = 800,
                                    ['depth9'] = {
                                        [1] = 900,
                                        ['depth10'] = {
                                            [1] = 1000
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    ['ted'] = {
        [1] = true,
        [2] = false,
        [3] = 'some text'
    }
}
Alundaio
  • 551
  • 4
  • 8
9

As previously mentioned, you have to write it. Here is my humble version: (super basic one)

function tprint (t, s)
    for k, v in pairs(t) do
        local kfmt = '["' .. tostring(k) ..'"]'
        if type(k) ~= 'string' then
            kfmt = '[' .. k .. ']'
        end
        local vfmt = '"'.. tostring(v) ..'"'
        if type(v) == 'table' then
            tprint(v, (s or '')..kfmt)
        else
            if type(v) ~= 'string' then
                vfmt = tostring(v)
            end
            print(type(t)..(s or '')..kfmt..' = '..vfmt)
        end
    end
end

example:

local mytbl = { ['1']="a", 2, 3, b="c", t={d=1} }
tprint(mytbl)

output (Lua 5.0):

table[1] = 2
table[2] = 3
table["1"] = "a"
table["t"]["d"] = 1
table["b"] = "c"
Totoka
  • 101
  • 2
  • 2
4

I use my own function to print the contents of a table but not sure how well it translates to your environment:

---A helper function to print a table's contents.
---@param tbl table @The table to print.
---@param depth number @The depth of sub-tables to traverse through and print.
---@param n number @Do NOT manually set this. This controls formatting through recursion.
function PrintTable(tbl, depth, n)
  n = n or 0;
  depth = depth or 5;

  if (depth == 0) then
      print(string.rep(' ', n).."...");
      return;
  end

  if (n == 0) then
      print(" ");
  end

  for key, value in pairs(tbl) do
      if (key and type(key) == "number" or type(key) == "string") then
          key = string.format("[\"%s\"]", key);

          if (type(value) == "table") then
              if (next(value)) then
                  print(string.rep(' ', n)..key.." = {");
                  PrintTable(value, depth - 1, n + 4);
                  print(string.rep(' ', n).."},");
              else
                  print(string.rep(' ', n)..key.." = {},");
              end
          else
              if (type(value) == "string") then
                  value = string.format("\"%s\"", value);
              else
                  value = tostring(value);
              end

              print(string.rep(' ', n)..key.." = "..value..",");
          end
      end
  end

  if (n == 0) then
      print(" ");
  end
end
Mayron
  • 2,146
  • 4
  • 25
  • 51
4

The simplest way, with circular reference handling and all:

function dump(t, indent, done)
    done = done or {}
    indent = indent or 0

    done[t] = true

    for key, value in pairs(t) do
        print(string.rep("\t", indent))

        if type(value) == "table" and not done[value] then
            done[value] = true
            print(key, ":\n")

            dump(value, indent + 2, done)
            done[value] = nil
        else
            print(key, "\t=\t", value, "\n")
        end
    end
end
Francisco
  • 431
  • 9
  • 16
3

There are 2 solutions that I want to mention: a quick&dirty one, and another which properly escapes all keys and values but is bigger

Simple & fast solution (use only on "safe" inputs):

local function format_any_value(obj, buffer)
    local _type = type(obj)
    if _type == "table" then
        buffer[#buffer + 1] = '{"'
        for key, value in next, obj, nil do
            buffer[#buffer + 1] = tostring(key) .. '":'
            format_any_value(value, buffer)
            buffer[#buffer + 1] = ',"'
        end
        buffer[#buffer] = '}' -- note the overwrite
    elseif _type == "string" then
        buffer[#buffer + 1] = '"' .. obj .. '"'
    elseif _type == "boolean" or _type == "number" then
        buffer[#buffer + 1] = tostring(obj)
    else
        buffer[#buffer + 1] = '"???' .. _type .. '???"'
    end
end

Usage:

local function format_as_json(obj)
    if obj == nil then return "null" else
        local buffer = {}
        format_any_value(obj, buffer)
        return table.concat(buffer)
    end
end

local function print_as_json(obj)
    print(_format_as_json(obj))
end

print_as_json {1, 2, 3}
print_as_json(nil)
print_as_json("string")
print_as_json {[1] = 1, [2] = 2, three = { { true } }, four = "four"}

Correct solution with key/value escaping

Small library that I wrote in pure Lua for this specific use-case: https://github.com/vn971/fast_json_encode

Or specifically this 1 file that includes both a formatter and a printer: https://github.com/vn971/fast_json_encode/blob/master/json_format.lua

VasiliNovikov
  • 9,681
  • 4
  • 44
  • 62
  • This is actually what I was looking for even though it wasn't specifically what the op was asking. Thanks for such as simple solution. Makes it easier to use in space-constrained Lua envs like NodeMCU. – Kevin Ghadyani Dec 25 '18 at 03:10
  • This will produce invalid JSON if keys or values contain quotes, newlines or control characters. – CherryDT Feb 11 '22 at 17:24
  • [@CherryDT](https://stackoverflow.com/users/1871033/cherrydt) Oh, good remark, thanks for that. I should definitely make it explicit that it doesn't escape anything (at all). For actually correct but slower solution, this should work: https://github.com/vn971/fast_json_encode/blob/master/json_format.lua Would love to hear any feedback if it works / doesn't work for you. – VasiliNovikov Feb 11 '22 at 21:27
3

Made this version to print tables with identation. Can probably be extended to work recursively.

function printtable(table, indent)
  print(tostring(table) .. '\n')
  for index, value in pairs(table) do 
    print('    ' .. tostring(index) .. ' : ' .. tostring(value) .. '\n')
  end
end
TiagoLr
  • 2,782
  • 22
  • 16
2

This is my version that supports excluding tables and userdata

-- Lua Table View by Elertan
table.print = function(t, exclusions)
    local nests = 0
    if not exclusions then exclusions = {} end
    local recurse = function(t, recurse, exclusions)
        indent = function()
            for i = 1, nests do
                io.write("    ")
            end
        end
        local excluded = function(key)
            for k,v in pairs(exclusions) do
                if v == key then
                    return true
                end
            end
            return false
        end
        local isFirst = true
        for k,v in pairs(t) do
            if isFirst then
                indent()
                print("|")
                isFirst = false
            end
            if type(v) == "table" and not excluded(k) then
                indent()
                print("|-> "..k..": "..type(v))
                nests = nests + 1
                recurse(v, recurse, exclusions)
            elseif excluded(k) then
                indent()
                print("|-> "..k..": "..type(v))
            elseif type(v) == "userdata" or type(v) == "function" then
                indent()
                print("|-> "..k..": "..type(v))
            elseif type(v) == "string" then
                indent()
                print("|-> "..k..": ".."\""..v.."\"")
            else
                indent()
                print("|-> "..k..": "..v)
            end
        end
        nests = nests - 1
    end

    nests = 0
    print("### START TABLE ###")
    for k,v in pairs(t) do
        print("root")
        if type(v) == "table" then
            print("|-> "..k..": "..type(v))
            nests = nests + 1
            recurse(v, recurse, exclusions)
        elseif type(v) == "userdata" or type(v) == "function" then
            print("|-> "..k..": "..type(v))
        elseif type(v) == "string" then
            print("|-> "..k..": ".."\""..v.."\"")
        else
            print("|-> "..k..": "..v)
        end
    end
    print("### END TABLE ###")
end

This is an example

t = {
    location = {
       x = 10,
       y = 20
    },
    size = {
      width = 100000000,
      height = 1000,
    },
    name = "Sidney",
    test = {
        hi = "lol",
    },
    anotherone = {
        1, 
        2, 
        3
    }
}

table.print(t, { "test" })

Prints:

   ### START TABLE ###
root
|-> size: table
    |
    |-> height: 1000
    |-> width: 100000000
root
|-> location: table
    |
    |-> y: 20
    |-> x: 10
root
|-> anotherone: table
    |
    |-> 1: 1
    |-> 2: 2
    |-> 3: 3
root
|-> test: table
    |
    |-> hi: "lol"
root
|-> name: "Sidney"
### END TABLE ###

Notice that the root doesn't remove exclusions

Elertan
  • 377
  • 3
  • 8
2

You have to code it yourself I'm afraid. I wrote this, and it may be of some use to you

function printtable(table, indent)

  indent = indent or 0;

  local keys = {};

  for k in pairs(table) do
    keys[#keys+1] = k;
    table.sort(keys, function(a, b)
      local ta, tb = type(a), type(b);
      if (ta ~= tb) then
        return ta < tb;
      else
        return a < b;
      end
    end);
  end

  print(string.rep('  ', indent)..'{');
  indent = indent + 1;
  for k, v in pairs(table) do

    local key = k;
    if (type(key) == 'string') then
      if not (string.match(key, '^[A-Za-z_][0-9A-Za-z_]*$')) then
        key = "['"..key.."']";
      end
    elseif (type(key) == 'number') then
      key = "["..key.."]";
    end

    if (type(v) == 'table') then
      if (next(v)) then
        printf("%s%s =", string.rep('  ', indent), tostring(key));
        printtable(v, indent);
      else
        printf("%s%s = {},", string.rep('  ', indent), tostring(key));
      end 
    elseif (type(v) == 'string') then
      printf("%s%s = %s,", string.rep('  ', indent), tostring(key), "'"..v.."'");
    else
      printf("%s%s = %s,", string.rep('  ', indent), tostring(key), tostring(v));
    end
  end
  indent = indent - 1;
  print(string.rep('  ', indent)..'}');
end
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 2
    Thanks for replying. I tried this and I get: attempt to call global 'sort' (a nil value) – Cliff Feb 06 '12 at 22:21
  • Change `sort` to `table.sort`... There must have been a `local sort = table.sort` somewhere in the code where this was taken from. – Michal Kottman Feb 06 '12 at 22:26
  • You have to be a bit imaginative! There are a number of symbols copied from the library table space to _G for convenience. `sort` is a copy of `table.sort`, `strrep` is `string.rep`, `strmatch` is `string.match` etc. Let me know if there are any more and I'll alter my answer. – Borodin Feb 06 '12 at 22:27
  • I'm sorry, I also have a pretty deep netting of tables as my own attempts to recurse the structure met with a stack overflow. (No pun intended!) I was banging my head trying to unwind my recursion and use proper tail calls but I got frustrated at which point I posted here. – Cliff Feb 06 '12 at 22:51
  • You can't in general remove the recursion from such a function, as it isn't end-recursive. Either use a Lua built with a bigger stack, or implement the same algorithm using a Lua table to store the recursion stack. – Borodin Feb 07 '12 at 11:08
  • 1
    Bad bad idea to use 'table' word as first function argument and then run `table.sort` - you just 'run' a field `sort` from the source table. What the hell, can you please rename the first argument? – kay27 Jul 04 '21 at 22:54
  • Also there is no reason to use `printf` which might be unavailable in Lua, you can always use print and concatenate the strings with two dots between them. I tried to edit the answer but got 'Suggested edit queue is full' error :( – kay27 Jul 04 '21 at 23:03
2

The table.tostring metehod of metalua is actually very complete. It deals with nested tables, the indentation level is changeable, ... See https://github.com/fab13n/metalua/blob/master/src/lib/metalua/table2.lua

kartben
  • 2,485
  • 21
  • 24
1

Adding another version. This one tries to iterate over userdata as well.

function inspect(o,indent)
    if indent == nil then indent = 0 end
    local indent_str = string.rep("    ", indent)
    local output_it = function(str)
        print(indent_str..str)
    end

    local length = 0

    local fu = function(k, v)
        length = length + 1
        if type(v) == "userdata" or type(v) == 'table' then
            output_it(indent_str.."["..k.."]")
            inspect(v, indent+1)
        else
            output_it(indent_str.."["..k.."] "..tostring(v))
        end
    end

    local loop_pairs = function()
        for k,v in pairs(o) do fu(k,v) end
    end

    local loop_metatable_pairs = function()
        for k,v in pairs(getmetatable(o)) do fu(k,v) end
    end

    if not pcall(loop_pairs) and not pcall(loop_metatable_pairs) then
        output_it(indent_str.."[[??]]")
    else
        if length == 0 then
            output_it(indent_str.."{}")
        end
    end
end
phil294
  • 10,038
  • 8
  • 65
  • 98
1

Convert to json and then print.

    local json = require('cjson')
    json_string = json.encode(this_table)
    print (json_string)
  • Certain Lua values (like functions) cannot be encoded into a json string. Most libraries will throw an error when they find such values. This solution is not generic enough to handle something as simple as `{a = print}` – kikito Sep 27 '22 at 12:00
1
--~ print a table
function printTable(list, i)

    local listString = ''
--~ begin of the list so write the {
    if not i then
        listString = listString .. '{'
    end

    i = i or 1
    local element = list[i]

--~ it may be the end of the list
    if not element then
        return listString .. '}'
    end
--~ if the element is a list too call it recursively
    if(type(element) == 'table') then
        listString = listString .. printTable(element)
    else
        listString = listString .. element
    end

    return listString .. ', ' .. printTable(list, i + 1)

end


local table = {1, 2, 3, 4, 5, {'a', 'b'}, {'G', 'F'}}
print(printTable(table))

Hi man, I wrote a siple code that do this in pure Lua, it has a bug (write a coma after the last element of the list) but how i wrote it quickly as a prototype I will let it to you adapt it to your needs.

Joqus
  • 585
  • 5
  • 19
0

simple example of dump a table in lua

i suggest using serpent.lua

local function parser(value, indent, subcategory)
  local indent = indent or 2
  local response = '(\n'
  local subcategory = type(subcategory) == 'number' and subcategory or indent
  for key, value in pairs(value) do
    if type(value) == 'table' then
      value = parser(value, indent, subcategory + indent)

    elseif type(value) == 'string' then
      value = '\''.. value .. '\''

    elseif type(value) ~= 'number' then
      value = tostring(value)
    end

    if type(tonumber(key)) == 'number' then
        key = '[' .. key .. ']'
   elseif not key:match('^([A-Za-z_][A-Za-z0-9_]*)$') then
      key = '[\'' .. key .. '\']'
    end
    response = response .. string.rep(' ', subcategory) .. key .. ' = ' .. value .. ',\n'
  end
  return response .. string.rep(' ', subcategory - indent) .. ')'

end 

example

response = parser{1,2,3, {ok = 10, {}}}
print(response)

result

(
  [1] = 1,
  [2] = 2,
  [3] = 3,
  [4] = (
    [1] = (),
    ok = 10
  )
)
milad
  • 21
  • 2
0

here's my little snippet for that:

--- Dump value of a variable in a formatted string
--
--- @param o    table       Dumpable object
--- @param tbs  string|nil  Tabulation string, '  ' by default
--- @param tb   number|nil  Initial tabulation level, 0 by default
--- @return     string
local function dump(o, tbs, tb)
  tb = tb or 0
  tbs = tbs or '  '
  if type(o) == 'table' then
    local s = '{'
    if (next(o)) then s = s .. '\n' else return s .. '}' end
    tb = tb + 1
    for k,v in pairs(o) do
      if type(k) ~= 'number' then k = '"' .. k .. '"' end
      s = s .. tbs:rep(tb) .. '[' .. k .. '] = ' .. dump(v, tbs, tb)
      s = s .. ',\n'
    end
    tb = tb - 1
    return s .. tbs:rep(tb) .. '}'
  else
    return tostring(o)
  end
end
-1

I have humbly modified a bit Alundaio code:

-- by Alundaio
-- KK modified 11/28/2019

function dump_table_to_string(node, tree, indentation)
    local cache, stack, output = {},{},{}
    local depth = 1


    if type(node) ~= "table" then
        return "only table type is supported, got " .. type(node)
    end

    if nil == indentation then indentation = 1 end

    local NEW_LINE = "\n"
    local TAB_CHAR = " "

    if nil == tree then
        NEW_LINE = "\n"
    elseif not tree then
        NEW_LINE = ""
        TAB_CHAR = ""
    end

    local output_str = "{" .. NEW_LINE

    while true do
        local size = 0
        for k,v in pairs(node) do
            size = size + 1
        end

        local cur_index = 1
        for k,v in pairs(node) do
            if (cache[node] == nil) or (cur_index >= cache[node]) then

                if (string.find(output_str,"}",output_str:len())) then
                    output_str = output_str .. "," .. NEW_LINE
                elseif not (string.find(output_str,NEW_LINE,output_str:len())) then
                    output_str = output_str .. NEW_LINE
                end

                -- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
                table.insert(output,output_str)
                output_str = ""

                local key
                if (type(k) == "number" or type(k) == "boolean") then
                    key = "["..tostring(k).."]"
                else
                    key = "['"..tostring(k).."']"
                end

                if (type(v) == "number" or type(v) == "boolean") then
                    output_str = output_str .. string.rep(TAB_CHAR,depth*indentation) .. key .. " = "..tostring(v)
                elseif (type(v) == "table") then
                    output_str = output_str .. string.rep(TAB_CHAR,depth*indentation) .. key .. " = {" .. NEW_LINE
                    table.insert(stack,node)
                    table.insert(stack,v)
                    cache[node] = cur_index+1
                    break
                else
                    output_str = output_str .. string.rep(TAB_CHAR,depth*indentation) .. key .. " = '"..tostring(v).."'"
                end

                if (cur_index == size) then
                    output_str = output_str .. NEW_LINE .. string.rep(TAB_CHAR,(depth-1)*indentation) .. "}"
                else
                    output_str = output_str .. ","
                end
            else
                -- close the table
                if (cur_index == size) then
                    output_str = output_str .. NEW_LINE .. string.rep(TAB_CHAR,(depth-1)*indentation) .. "}"
                end
            end

            cur_index = cur_index + 1
        end

        if (size == 0) then
            output_str = output_str .. NEW_LINE .. string.rep(TAB_CHAR,(depth-1)*indentation) .. "}"
        end

        if (#stack > 0) then
            node = stack[#stack]
            stack[#stack] = nil
            depth = cache[node] == nil and depth + 1 or depth - 1
        else
            break
        end
    end

    -- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
    table.insert(output,output_str)
    output_str = table.concat(output)

    return output_str

end

then:

print(dump_table_to_string("AA", true,3))

print(dump_table_to_string({"AA","BB"}, true,3))

print(dump_table_to_string({"AA","BB"}))

print(dump_table_to_string({"AA","BB"},false))

print(dump_table_to_string({"AA","BB",{22,33}},true,2))

gives:

only table type is supported, got string

{
   [1] = 'AA',
   [2] = 'BB'
}

{
 [1] = 'AA',
 [2] = 'BB'
}

{[1] = 'AA',[2] = 'BB'}

{
  [1] = 'AA',
  [2] = 'BB',
  [3] = {
    [1] = 22,
    [2] = 33
  }
}

kris2k
  • 325
  • 3
  • 5
-2

Now the function print can print the (flat) tables!

oprint = print -- origin print 
print = function (...)
    if type(...) == "table" then
        local str = ''
        local amount = 0
        for i,v in pairs(...) do
            amount=amount+1
            local pre = type(i) == "string" and i.."=" or ""
            str = str .. pre..tostring(v) .. "\t"
        end
        oprint('#'..amount..':', str)
    else
        oprint(...)
    end
end

For example:

print ({x=7, y=9, w=11, h="height", 7, 8, 9})

prints:

#7: 7 8 9 y=9 x=7 h=height w=11

The same way it can be just new function tostring:

otostring = tostring -- origin tostring
tostring = function (...)
    if type(...) == "table" then
        local str = '{'
        for i,v in pairs(...) do
            local pre = type(i) == "string" and i.."=" or ""
            str = str .. pre..tostring(v) .. ", "
        end
        str = str:sub(1, -3)
        return str..'}'
    else
        return otostring(...)
    end
end
darkfrei
  • 122
  • 5