Get next item in table from a set item

Backpacks = {
		Default = {Cost = 0, Storage = 25},
		Backpack1 = {Cost = 50, Storage = 50},
		Backpack2 = {Cost = 100, Storage = 100},
	}

local PlayersBackpack = CheckBackpack:InvokeServer() -- returns 'Default'
local NextBackpack = Backpacks[PlayersBackpack] + 1

I’m trying to basically get the next backpack in the list, based on the players backpack. So if the players backpack is Default, then NextBackpack should be Backpack1, and so on, until they have the final backpack

Call the Lua global next. I modified your code a little to fit a repro and tested it in Studio.

local Backpacks = {
	Default = {Cost = 0, Storage = 25},
	Backpack1 = {Cost = 50, Storage = 50},
	Backpack2 = {Cost = 100, Storage = 100},
}

local PlayersBackpack = "Default"
print(PlayersBackpack, Backpacks[PlayersBackpack]) --> Default, table (Default)
local NextBackpack = next(Backpacks, PlayersBackpack)
print(NextBackpack, Backpacks[NextBackpack]) --> Backpack1, table (Backpack1)

Fun fact: iteration via (i)pairs uses the next function to traverse through each element in a table. You can keep going until you get a nil value back.

Note: you may need to sort keys. You don’t use numerical indices, so the order isn’t concrete. It came out as Backpack2, Default, Backpack1.

I can’t then reference tables from within it?

print(NextBackpack) -- prints Backpack1
print(NextBackpack.Cost) -- prints nil

That’s because what you get back as the first argument is the key. To access the table, add a second variable. I wasn’t expecting that you were going to make a direct call like that.

local Backpacks = {
	Default = {Cost = 0, Storage = 25},
	Backpack1 = {Cost = 50, Storage = 50},
	Backpack2 = {Cost = 100, Storage = 100},
}

local PlayersBackpack = "Default"
local NextBackpack, BackpackData = next(Backpacks, PlayersBackpack)
print(BackpackData.Cost) --> 50

It breaks when I unlock the next backpack tho :confused:

Backpacks = {
		Default = {Cost = 0, Storage = 25},
		Backpack1 = {Cost = 50, Storage = 50},
		Backpack2 = {Cost = 100, Storage = 100},
	}

local PlayersBackpack = CheckBackpack:InvokeServer()
local NextBackpack, BackpackData = next(Backpacks, PlayersBackpack)
print(PlayersBackpack, NextBackpack, BackpackData) --  Backpack1 nil nil

It’s hard to tell from your code there, but most likely what’s happening is that the Backpacks table is not in the order that you think it is.

In a simplified way, there are two types of tables:

  1. Arrays. These are essentially lists which stay in an order that you set. You have to get each item in an array using a number, just like a list!
  2. Dictionaries. These map a key to a value, like a dictionary maps words to definitions. These are “unordered” – they do not stay in the order you set.

What you have here is a dictionary: you’re mapping the backpack name to the backpack data. You think that it’s in this order…

{
	Default = {Cost = 0, Storage = 25},
	Backpack1 = {Cost = 50, Storage = 50},
	Backpack2 = {Cost = 100, Storage = 100},
}

…but really, it could be in this order…

{
	Backpack2 = {Cost = 100, Storage = 100},
	Default = {Cost = 0, Storage = 25},
	Backpack1 = {Cost = 50, Storage = 50},
}

…or in any other order! You can’t rely on dictionaries to stay in the order you want. You need an array for that.


To fix this issue, you will need to get this into array form first. There are many ways to do this depending on your needs. One nice option is the following:

local BackpacksArray = {
	{Name = 'Default', Cost = 0, Storage = 25},
	{Name = 'Backpack1', Cost = 50, Storage = 50},
	{Name = 'Backpack2', Cost = 100, Storage = 100},
}

local BackpacksDict = {}

for index, backpack in pairs(BackpacksArray) do
	backpack.Index = index
	BackpacksDict[backpack.Name] = backpack
end

-- Examples:
print(BackpacksArray[1].Name) --> Default
print(BackpacksDict.Default.Storage) --> 25

-- Example getting the next backpack:
local MyBackpack = BackpacksDict.Default
local NextBackpack = BackpacksArray[MyBackpack.Index + 1]
print(NextBackpack.Name) --> Backpack1

This gives you an ordered list of backpacks using BackpacksArray and a way to get backpacks by name using BackpacksDict. The first backpack is item 1 in the list, or BackpacksArray[1]. Each backpack stores its position in the list as Index. To get to the next backpack, you just get the backpack at the next position in the list: BackpacksArray[MyBackpack.Index + 1]

4 Likes

I’ve tried this with a new set of data and it doesn’t work anymore:

Tails = {
		[1] = {Cost = 0},
		[2] = {Cost = 250},
		[3] = {Cost = 500},
		[4] = {Cost = 1000},
		[5] = {Cost = 2500},
		[6] = {Cost = 5000},
	}

local PlayersTail = CheckTail:InvokeServer()
local NextTail, TailData = next(Tails, PlayersTail)
print(PlayersTail, NextTail, TailData)

Prints
[1 4 table: 0xd7930bf5d82d4b4c]
So the players tail is 1, but it’s saying Next is 4??

This is an odd issue. I tried playing around with this in Studio and I got some unexpected results, so I threw in some code to try and diagnose the problem. Specifically, I added a for loop (pairs) to see what was happening.

Tails = {
		[1] = {Cost = 0},
		[2] = {Cost = 250},
		[3] = {Cost = 500},
		[4] = {Cost = 1000},
		[5] = {Cost = 2500},
		[6] = {Cost = 5000},
	}

local PlayersTail = 1
local NextTail, TailData = next(Tails, PlayersTail)
print(PlayersTail, NextTail, TailData)

for i,v in pairs(Tails) do
	print(i, v)
end

For whatever reason, your table’s order is as follows: 6, 2, 3, 1, 4, 5. When using ipairs however, since you’re using numeric keys in your Tails table, it comes out as expected: 1-6.

I don’t know what your code looks like right now but it seems that you’ve decided to switch over to numeric keys. If you’re keeping it that way, good. Let’s do this: instead of using the next function, assume that whatever numbers you have are your keys. Then to get the next value, just add.

Tails = {
		[1] = {Cost = 0},
		[2] = {Cost = 250},
		[3] = {Cost = 500},
		[4] = {Cost = 1000},
		[5] = {Cost = 2500},
		[6] = {Cost = 5000},
	}

local PlayersTail = 1
local NextTail = PlayersTail + 1
local TailData = Tails[NextTail]
print(PlayersTail, NextTail, TailData)

Then, if TailData is nil, you know that there’s no value for the indice Tails[NextTail]. In addition, because you have numeric indices, you can use the length (#) operator to get the table size, if you ever need it. Printing out #Tails gives 6. Then you could do… I don’t know, whatever math you need.

local NextTail = math.max(#Tails, PlayersTail + 1) -- Clamps to a max
2 Likes