Data Store "stringifies" array indexes if array doesn't start from index 1, and behaves incosistently when array has missing indexes

When trying to save an array that doesn’t have the index/key 1 defined, the array’s keys turn to strings when retrieved using GetAsync.

Reproduction Steps:
Create a new experience and enable studio access to API Services

Run the following script/line using the Command Bar:

game:GetService("DataStoreService"):GetDataStore("SomeDataStore"):SetAsync("Key", {[2] = "a", [3] = "b"})

Now get the data back using GetAsync, also using the command bar:

print(game:GetService("DataStoreService"):GetDataStore("SomeDataStore"):GetAsync("Key"))

image
You’ll find that the indexes/keys of the array are now strings instead of numbers.


In addition, if that array does contain the key/index 1, but the array has a missing key-value pair inside it, in some cases, only a part of the array is stored.

Reproduction Steps:
Run the following script/line using the Command Bar:

--Notice how index/key 2 is missing, watch what happens when the data is retrieved:
game:GetService("DataStoreService"):GetDataStore("SomeDataStore"):SetAsync("Key", {[1] = "a", [3] = "b"})

Now get the data back using GetAsync, also using the command bar:

print(game:GetService("DataStoreService"):GetDataStore("SomeDataStore"):GetAsync("Key"))

image
Key/Index 3 is no longer in the array, for some reason.

I tried adding a 4th key to the array with a missing 2nd key/index. For some reason, it works in that case:

game:GetService("DataStoreService"):GetDataStore("SomeDataStore"):SetAsync("Key", {[1] = "a", [3] = "b", [4] = "c"})
print(game:GetService("DataStoreService"):GetDataStore("SomeDataStore"):GetAsync("Key"))

This behavior seems to be inconsistent. I couldn’t find any documentation on the above occurrences.

System information:
image
image

Expected behavior

I expect the array to be stored exactly like I stored it.
Which means, keys/indexes should still be numbers if index/key 1 is missing, and values shouldn’t be emitted from the array if the array has a missing key/index inside it.

The problem is that there are holes in your array, which can lead to undefined behavior. Arrays are supposed to be filled to the brim with values, meaning there shouldn’t be any holes.

If there are holes in the array, it might be treated as a dictionary. The engine also converts all non array indices to strings.

2 Likes

It has to do with json encoding I’m pretty sure. Like @VegetationBush said, anything not starting from 1 or with holes in the indexing converts to a dictionary because the indexing data would be lost if it were stored as an array in json formatting.

For example if you tried to save {[2] = true} as an array in json, it would be [true] which doesn’t include the position. So instead, it is saved like [[“2”] = true]. (the formatting is probably off I don’t remember json too well)

As for the data loss, I’m not sure what processes cause that! My guess is that when saving arrays, it iteratively goes through each value until the first gap is reached and ignores the rest. But if the first value doesn’t exist, it counts it as a dictionary.

1 Like

This isn’t a bug, this is intended, and if I am not mistaken this also happens when transferring arrays over remotes.

The reason this is intended, as @VegetationBush stated, is that breaking the indexing of an array causes issues such as the breaking of certain table manipulation functions (table.remove, table.len/#) and hence Roblox converts the array into a string index based table.

My question is why is this such an issue to you? All developers seem to get around it fine and honestly I rarely use arrays anyways since indexing in them can be a pain.

1 Like

Thank you for the reply.

Where is this documented as intended behavior?
I reported this since I could not find a single thread mentioning this.

I’ve spent an hour trying to figure out why trying to index the array using numbers didn’t work when I clearly saved them as such.

If this isn’t a bug, then at the very least it should be documented clearly in the datastore page, and not in any other page like remote events, where there is very little chance anyone would remotely find it there.

Where is this stated?

This is an issue because I could not find this behavior documented anywhere in the datastores page. I’ve spent time trying to figure out why my array wasn’t retrieved properly. If this isn’t written anywhere, then other developers would get confused as well.

Hello, thank you :slightly_smiling_face:
I would expect that if there’s a gap it would be replaced with nil. That does happen when there are 4 indexes for example, just not with 3, which is inconsistent (and doesn’t make much sense).

Thank you, that seems to explain the keys turning to strings.
I guess I’d have to go around that with using tostring to get the values.

1 Like

Yeah not sure about the inconsistencies :frowning: I had this issue with my inventory system but I found that replacing empty slots with a nil placeholder, like “” or “_” was easier than converting to strings every time

I couldn’t either, there is this warning in the remote events section:

Table indexing
If you pass a table of data, do not pass a mixed table of numeric and string keys. Instead, pass a table that consists entirely of key-value pairs (dictionary) or entirely of numeric indices.

Whether passing a dictionary table or a numerically indexed table, avoid nil values for any index.

This isn’t the first time someone has had an issue with this ‘feature’

Yes, and that’s unfortunately why arrays shouldn’t have nil values (because the behavior is undefined). I’ve run into the same issue before, so maybe these responses may help you understand a little better:

1 Like

As with many other engine systems, the only tables that DataStores support are arrays (integer keys without any nil gaps) and maps with string keys.

Anything else such as including gaps or mixing string and integer keys is undefined behavior.

FWIW Roblox documentation accepts community contributions. There’s a button on the applicable pages. Your contribution needs to be high quality to be accepted though, not just lazily thrown in.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.