How to find Neighboring parts?

What would be the best way to : get neighboring parts for every part and do I have to make instance for each part or is there a better way, maybe tables I don’t know ?
gridexample

Example for a gui that has each slot with a name like: “1” or “123”

local function CreateNeighbor(slot, neighboringSlot, direction)
	local objectValue = Instance.new("ObjectValue")
	objectValue.Name = direction
	objectValue.Value = neighboringSlot
	objectValue.Parent = slot.Neighbors
end

function Grid.Setup(gui)
	


	for i = 1,SlotsX*SlotsY do
		local slot = gui.Grid[tostring(i)]
		if gui.Grid:FindFirstChild(tostring(i-1)) and (i-1)%15 ~= 0 then -- // Is there another slot above?
			CreateNeighbor(slot, gui.Grid[tostring(i-1)], "Up")
		end

		if i%15 ~= 0 and gui.Grid:FindFirstChild(tostring(i+1)) then -- // Is there another slot below?
			CreateNeighbor(slot, gui.Grid[tostring(i+1)], "Down")			
		end

		if gui.Grid:FindFirstChild(tostring(i+SlotsY)) then -- // Is there another slot to the right?
			CreateNeighbor(slot, gui.Grid[tostring(i+SlotsY)], "Right")
		end

		if gui.Grid:FindFirstChild(tostring(i-SlotsY)) then -- // Is there another slot to the right?
			CreateNeighbor(slot, gui.Grid[tostring(i-SlotsY)], "Left")
		end
	end
end

If you are generating the parts from a script you can name them while generating them. Giving them names such as X:Y or X_Y makes it really easy to do stuff like this.

If you are not generating it from a script you might just have to write it out manually. You can of course generate these from a script, but it is a lot harder than just naming them yourself.