Mixed tables in Remote/Bindable Event/Function

Show me what the table looks like that you’re trying to send over?

I have multiple different tables that encounter this issue, this isn’t a problem limited to one specific table.

Why do you have mixed tables? There are few cases where I find mixed tables to actually be the right design choice. Could you break up these tables into nested tables with sequential and referential parts? What is just one example where you are having trouble sending a mixed table?

Ultimately, the issue probably lies in the fact that you are mixed data structures. A mixed index table isn’t really a true data structure. You are combining an array with a map and that really doesn’t make sense from a computer science perspective.

6 Likes

Yeah, I have to agree with @TheNickmaster21 on this. It’s pretty easy to design structures that avoid mixed tables.

I can’t think of any real-world applications where a mixed table-like structure is used.

2 Likes

It’s mostly nil values causing the issues, and the fact that you can’t do { { X = 1, X = 2 }, { X = 1, X = 2 }

You should be able to do that. A mixed table only relates to the immediate table, not sub-tables. I’ve done it many times with DataStores and such.

Interesting, not sure what was causing the error then! I’ll do some tests and post here when I’m done.

This is incorrect, all sub-tables (and sub-sub-tables, and sub-sub-sub, etc…) need to be non-mixed right now. The property applies to the entire structure of tables, not just top-level. (This is most likely because it is marshalled into JSON internally)

> workspace.Event:Fire(
   {
      "hello",
      {
         [2] = "world",
         [5] = "!!!"
      }
   }
)
18:02:44.507 - Cannot convert mixed or non-array tables: keys must be strings
1 Like

This is the table I’m having issues with:

{ [ 2 ] = { 1, 2 } }

Don’t have the issue with this though

{ nil, { 1, 2 } }

As a temporary workaround, if your keys can only be numbers, you can just tostring the indices that you set and then turn them back into numbers at the receiving end.

That’s because in the first example, you have an array of numerical indices but it doesn’t start at 1 (and also it may have no holes, if there were multiple indices). In the second example, you don’t supply any indices, so it just becomes an array which is allowed.

1 Like

Yet { 1, 2, 3, B = 1 } works?

I think I may be misunderstanding what a mixed table is?

If you actually print the result of that table being passed through a Bindable/RemoteEvent, you’d find that the B = 1 probably got cut off. That’s Roblox’s mistake for not throwing an error there.

2 Likes

Figured out I can use the following function to make it work for now:

local function Fill( Table, a, max )
	a = a or 1
	if not max then
		max = 1
		for a, b in pairs( Table ) do
			max = math.max( max, a )
		end
	end
	if a > max then return end
	return Table[ a ], Fill( Table, a + 1, max )
end

and then

Event:Fire( { Fill{ [ 2 ] = { 1, 2 } } )

-- Original
{ [ 2 ] = { 1, 2 } }

-- New
{ [ 2 ] = { 1, 2 } }

Ok, weird, that must be an issue with Remote/BindableEvents, because it definitely works in DataStores.

This code demonstrates the culling that will occur.

local bindable = Instance.new("BindableEvent")

local function printTable(tab, depth)
	depth = depth or 0
	local indent = string.rep("\t", depth)
	for i, v in pairs(tab) do
		if type(v) == "table" then
			print(indent .. i .. " : {")
			printTable(v, depth + 1)
			print(indent .. "}")
		else
			print(indent .. i .. ": " .. tostring(v))
		end	
	end
	
end

bindable.Event:Connect(printTable)
bindable:Fire({ 1, 2, 3, B = 1 })

Output:

1: 1
2: 2
3: 3
1 Like

Made a bug report for this

This is by design, the wiki states

If a Table is passed as an argument to a BindableEvent it must be an array without missing entries or have string keys, not a mixture, or else the string keys will be lost.

Personally feel like this is a bug, if it’s going to be culling keys it should either error or warn to let us know. It errors for other cases.

local tab = {};
tab[1] = 1;
tab[2] = 2;
tab[3] = 3;
-- tab[4] = 4;
tab[2] = nil;
print(game.HttpService:JSONEncode(tab));

Run this and it will give you [1]. It should give you [1, null, 3]. Now uncomment the commented line. It will give you [1, null, 3, 4] as expected.

Note that this affects DataStores and RemoteFunctions/RemoteEvents. Please fix soon.

Yet the following code works:

script.Event.Event:connect( function ( t )
	
	for a, b in pairs( t ) do print( a, b ) end
	
end )

script.Event:Fire( { 1, nil, 3 } )
>1 1
>3 3