Counting not working on this table?

I wrote an array where there are sub arrays.

local ShotgunsType = {
		
		Prototype = {
			{InstanceName = "PumpShotguns", Name = "Pump Shotguns"};
			{InstanceName = "CombatShotguns", Name = "Combat Shotguns"};
			{InstanceName = "DBShotguns", Name = "Double Barrel"};
			{InstanceName = "MagShotguns", Name = "Magazine Shotguns"};
			{InstanceName = "LeverShotguns", Name = "Lever Shotguns"};
			{InstanceName = "LegendaryShotguns", Name = "Legendary Shotguns"};
		};
		
		benson = {
			PumpShotgunsType = {
				{InstanceName = "TypicalPumpShotgun", Name = "Typical Pump"};
				{InstanceName = "CommonPumpShotgun", Name = "Common Pump"};
				{InstanceName = "UncommonPumpShotgun", Name = "Uncommon Pump"};
				{InstanceName = "LegendaryPumpShotgun", Name = "Legendary Pump"};
				{UILocation = UI.ShotgunFrames.PumpShotgunsFrame};
			};
			
		    CombatShotgunsType = {
				{InstanceName = "TypicalCombatShotgun", Name = "Typical Combat"};
				{UILocation = UI.ShotgunFrames.CombatShotgunsFrame};
			};
		
		    DBShotgunsType = {
				{InstanceName = "TypicalDB", Name = "Typical Double Barrel"};
				{UILocation = UI.ShotgunFrames.DBShotgunsFrame};
			};
		
		    MagShotgunsType = {
				{InstanceName = "TypicalMagshotgun", Name = "Typical Magazine Shotgun"};
				{InstanceName = "CommonMagShotgun", Name = "Common Magazine Shotgun"};
				{InstanceName = "UncommonMagShotgun", Name = "Uncommon Magazine Shotgun"};
				{InstanceName = "LegendaryMagShotgun", Name = "Legendary Magazine Shotgun"};
				{UILocation = UI.ShotgunFrames.MagShotgunsFrame};
			};
		
		    LeverShotgunsType = {
				{InstanceName = "TypicalLeverShotgun", Name = "Typical Lever Shotgun"};
				{UILocation = UI.ShotgunFrames.LeverShotgunsFrame};
			};
		
			LegendaryShotgunsType = {
				{InstanceName = "LegendaryPumpShotgun", Name = "Legendary Pump"};
				{InstanceName = "LegendaryMagShotgun", Name = "Legendary Magazine"};
				{UILocation = UI.ShotgunFrames.LegendaryShotgunsFrame};
			};
		};
	}

When I tried to print #ShotgunType.benson, it should result in 6 because there are 6 sub arrays:
image
However, it results in 0, what’s the reason behind? I think I might have a misconception.
image

“The length operator returns the last numeric key whose value is not nil for tables starting from 1. The length operator returns zero for tables when the first numeric key is nil or there are no numeric keys.”
(Source: https://en.wikibooks.org/wiki/Lua_Programming/length_operator)

So the # operator will for example work in these two cases

local a = {"item1", "item2", ...}

or

local a = {}
table.insert(a, "item1")
...

But benson is sort of a dictionary with string as index and table as value and the # operator won’t work in that case and you have to get the length manually.

function len(tab)
    local sum = 0
    for _, v in pairs(tab) do
        sum = sum + 1
    end
    return sum
end
6 Likes

As @Kacper said, here is another weird example, let’s say you had a mixed table which has some dictionarries and some indexes (indices).

local something = {hi = 2, bye = "ok", true, 38, yo = "hmm"}

print(something[2]) 
--at first you'd thing that it would print "ok", meaning
--we are refering to bye
--but no! It actually prints 38, because indices and dictionarries are 2
--seperate things, 38 is represented by the index 2 while "ok" is 
--represented by the key "bye" and not by its placement

print(something[2]) --this is 38, because the value 38 takes place in the 2nd index
print(something.bye) --the value "ok" takes place inside of key, which we would refer to this way
--also something.bye is the same as something["bye"]

print(#something) --prints 2 because we have only 2 indices, dictionarries don't count
4 Likes

How about this? I tried to print the count the dictionary but it results 0.

local MapConfigs = {
	
	Map1_Configs = {
		location = game.ReplicatedStorage.Maps.Map1;
		Map1Name = "Construction Site";
		
		LightingSettings = {
			ClockTime = 8.9;
			GeographicLatitude = 41;
			ColorShift_Top = Color3.fromRGB(255,255,255);
			
			SkyboxBk = "http://www.roblox.com/asset/?version=1&id=133184290";
			SkyboxDn = "http://www.roblox.com/asset/?version=1&id=133184311";
			SkyboxFt = "http://www.roblox.com/asset/?version=1&id=133184318";
			SkyboxLf = "http://www.roblox.com/asset/?version=1&id=133184299";
			SkyboxRt = "http://www.roblox.com/asset/?version=1&id=133184333";
			SkyboxUp = "http://www.roblox.com/asset/?version=1&id=133184341";
		};
	};
};

print(#MapConfigs.Map1_Configs.LightingSettings) results in 0? There’s no dictionary inside LightingSettings though.

Also tried
print(MapConfigs.Map1_Configs.LightingSettings[2])
which results nil, I expect it to be 41?

LightingSettings is the dictionary. It does not matter if it has a dictionary or not in itself. Dictionary means a table with keys that are not sequential or numeric. You can often identify a dictionary by checking if it has name = value or [name] = value. Like @starmaq showed, when the table construction just has simple values like {123, true} (notice that it does not have name = value), then it is an array, the name given to tables that have only numeric and sequential keys starting from 1, and that are supported by the length operator (#). There is no good way to get the length of dictionaries besides using the idea that @Kacper presented in their above reply that utilizes pairs.

If you for example did print(#ShotgunsType.benson.PumpShotgunsType), it would print 5 because it has PumpShotgunsType = {{}, {}, {}, {}, {}} (it does not matter if it has dictionaries or not, just the fact that the keys of this table are sequential and are not just name = value). When constructing tables like this, the keys are numeric, starting from 1, so ShotgunsType.benson.PumpShotgunsType[1] would actually get the value of the first index, which happens to be a dictionary, then you could also do print(ShotgunsType.benson.PumpShotgunsType[1].Name) and it would print the name in the dictionary of the first index in PumpShotgunType.

LightingSettings does not have any numeric keys, and returns nil when you try to do for example LightingSettings[1], because you have defined all fields using name = value, and so you can get the keys using LightingSettings.field_name instead, for example LightingSettings.ClockTime.

If you think about, it does not really make sense to index something called “LightingSettings” using numbers, or to get the length of it either. It does make sense, however, to index it using named keys, like LightingSettings.ClockTime, which is more convenient and simpler to remember.

1 Like

Thanks for the detailed explanation, I came over to another problem:

Let’s say there’s a table with dictionaries:

local MapConfigs = {
	
	Map1_Configs = {
		location = game.ReplicatedStorage.Maps.Map1;
		MapName = "Construction Site";
		
		LightingSettings = {
			ClockTime = 8.9;
			GeographicLatitude = 40;
			ColorShift_Top = Color3.fromRGB(255,255,255);
			
			SkyboxBk = "http://www.roblox.com/asset/?version=1&id=133184290";
			SkyboxDn = "http://www.roblox.com/asset/?version=1&id=133184311";
			SkyboxFt = "http://www.roblox.com/asset/?version=1&id=133184318";
			SkyboxLf = "http://www.roblox.com/asset/?version=1&id=133184299";
			SkyboxRt = "http://www.roblox.com/asset/?version=1&id=133184333";
			SkyboxUp = "http://www.roblox.com/asset/?version=1&id=133184341";

		};
	};
	
	Map2_Configs = {
		location = game.ReplicatedStorage.Maps.Map2;
		MapName = "Containers Dock";
		
		LightingSettings = {
			ClockTime = 8.9;
			GeographicLatitude = 41;
			ColorShift_Top = Color3.fromRGB(255,170,0);
			
			SkyboxBk = "http://www.roblox.com/asset/?id=458016711";
			SkyboxDn = "http://www.roblox.com/asset/?id=458016826";
			SkyboxFt = "http://www.roblox.com/asset/?id=458016532";
			SkyboxLf = "http://www.roblox.com/asset/?id=458016655";
			SkyboxRt = "http://www.roblox.com/asset/?id=458016782";
			SkyboxUp = "http://www.roblox.com/asset/?id=458016792";
		}
	}
}

If I want to print MapName without knowing it is Map1 or Map2, how do I perform that?
I can not (MapConfigs[1].MapName) since MapConfigs[1] is nil because it’s a dictionary.

1 Like

If you have another need for this try table.length(tableName)

Edit: My bad, I was thinking about JavaScript. Its table.getn(tableName)

Not worth suggesting. The length operator already accomplishes the adequately and I don’t think the length function even exists for the table library as far as RLua goes. Even if it did, don’t know why you’d pick it over the length operator.

1 Like

Probably one of the easiest ways to accomplish this dynamically is to iterate over your dictionary and create a new table as a proxy. This one would be an array where the values are the keys of each dictionary.

local function dictionaryKeysToArray(dictionary)
    local array = {}

    for key, _ in pairs(dictionary) do
        table.insert(array, key)
    end

    return array
end

From there, you will have two tables: one that is an array full of the keys of your dictionary and another where the same values are present but as keys and the value is configuration data. From there, you can perform two table lookups: the first a random to get a map, the second to get the map data itself.

local MapNames = dictionaryKeysToArray(MapConfigs)
local RNG = Random.new()

local mapKey = MapNames[RNG:NextInteger(1, #MapNames)]
local mapConfig = MapConfigs[mapKey]
2 Likes

Similar to what I just did, thanks!