How to dump a table to console?

Lua

Lua Problem Overview


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.

Lua Solutions


Solution 1 - Lua

If the requirement is "quick and dirty"

I've found this one useful. Because if 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,} ,}

Solution 2 - Lua

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!

Solution 3 - Lua

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
  }
}

Solution 4 - Lua

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...

Solution 5 - Lua

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'
	}
}

Solution 6 - Lua

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"

Solution 7 - Lua

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

Solution 8 - Lua

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

Solution 9 - Lua

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

Solution 10 - Lua

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

Solution 11 - Lua

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

Solution 12 - Lua

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

Solution 13 - Lua

Convert to json and then print.

	local json = require('cjson')
	json_string = json.encode(this_table)
	print (json_string)

Solution 14 - Lua

--~ 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.

Solution 15 - Lua

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

Solution 16 - Lua

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
  )
)

Solution 17 - Lua

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

Solution 18 - Lua

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
  }
}

Solution 19 - Lua

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCliffView Question on Stackoverflow
Solution 1 - LuahookenzView Answer on Stackoverflow
Solution 2 - LuakikitoView Answer on Stackoverflow
Solution 3 - LuaMichal KottmanView Answer on Stackoverflow
Solution 4 - LuaJCH2kView Answer on Stackoverflow
Solution 5 - LuaAlundaioView Answer on Stackoverflow
Solution 6 - LuaTotokaView Answer on Stackoverflow
Solution 7 - LuaFranciscoView Answer on Stackoverflow
Solution 8 - LuaVasiliNovikovView Answer on Stackoverflow
Solution 9 - LuaMayronView Answer on Stackoverflow
Solution 10 - LuaBorodinView Answer on Stackoverflow
Solution 11 - LuakartbenView Answer on Stackoverflow
Solution 12 - LuaElertanView Answer on Stackoverflow
Solution 13 - LuaRajitha WijayaratneView Answer on Stackoverflow
Solution 14 - LuaJoqusView Answer on Stackoverflow
Solution 15 - Luaphil294View Answer on Stackoverflow
Solution 16 - LuamiladView Answer on Stackoverflow
Solution 17 - LuaTiagoLrView Answer on Stackoverflow
Solution 18 - Luakris2kView Answer on Stackoverflow
Solution 19 - LuadarkfreiView Answer on Stackoverflow