Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Why is this loop only looping twice even though the length of array is three at the start?

I am working on debugging an algorithm I wrote and part of it has an error that is incomprehensible to me. The only way I discovered it was by printing, because it was not throwing any errors in the output. Before getting to deep into this issue I would like to mention that EgoMoose already solved my issue by recommending table.sort() and using both the first and second argument. That being said, I would still like to find out why my function was not working as intended. I don't think that this first function I will post has any relationship to the problem, but I will post it just to make sure (and satisfy the curious among you all). Note: I will likely be changing a lot of these functions because EgoMoose revealed the incredibly useful second parameter of table.sort(). I just want to find out the elusive logic error in my code so that I do not repeat it in the future. Here is the first function:

local function findMatches(recipes, inputDictionary)
	 --[[ 
		recipes is an array and I will reveal
		its structure at the end of this question. 
		inputDictionary is just a dictionary of words
		retrieved from a lowercased string input by a user.
	--]]
	local newRecipeList = {unpack(recipes)} 
	--[[
		This is so that I don't mess with the 
		original recipes array.
	--]]
	local matchesList = {}
	for i, recipe in pairs(newRecipeList) do
		local ingredientsList = recipe.Ingredients 
		--[[
			Note: In Lua recipe.Ingredients
			is the same as recipe["Ingredients"]
		--]]
		if ingredientsList then
			local matchesAmount = 0
			for i,v in pairs(ingredientsList) do
				if inputDictionary[i:lower()] then
					matchesAmount = matchesAmount + 1
				end
			end
			if matchesAmount > 0 then
				recipe["Amount"] = matchesAmount
				table.insert(matchesList, recipe)
			end
		end
	end
	return sortByLargest(matchesList) 
	--[[
		sortByLargest() is the function that
		is causing me problems I just wanted
		to provide this function so that you 
		could see where "array" was coming 
		from.
	--]]
end

As far as I know it is working correctly. The function that causes me the most problems is this one:

local function sortByLargest(array) 
	print(#array) -- this prints 3
	local greatestArray = {}
	while #array > 0 do
		print("Looped")
		local indexOfGreatestSoFar, greatestSoFar = next(array)
		for i,v in pairs(array) do
			print(v.Amount.." "..v.Name)
			--[[
				The output from these 
				prints is as follows:
				Looped
				1 Beef and Cheese
				1 Beef Cheese
				1 Beef and flower
				Looped
				1 Beef Cheese
				1 Beef and flower
				Beef and Cheese
				Beef Cheese
				As you can see
				it is only looping twice
				when #array is 3
			--]]
			if v.Amount > greatestSoFar.Amount then
				greatestSoFar = v
				indexOfGreatestSoFar = i
			end
		end
		table.insert(greatestArray, greatestSoFar)
		array[indexOfGreatestSoFar] = nil
	end
	return sortByUpvotes(greatestArray)
end

I hope that you can spot some kind of error or something in that function of mine. The structure of the recipes array that is passed as an argument to the recipes parameter is thus:

local exampleListOfRecipeTypes = {
	{
		["Name"] = "Beef and Cheese";
		["Upvotes"] = 0;
		["Ingredients"] = {
			["Salt"] = "1 tsp";
			["Beef"] = "1 pound";
		};
	};
	
	{
		["Name"] = "Beef Cheese";
		["Upvotes"] = 0;
		["Ingredients"] = {
			["Salt"] = "1 tsp";
			["Beef"] = "2 pounds"; 	
		};
	};
	
	{
		["Name"] = "Beef and flower";
		["Upvotes"] = 0;
		["Ingredients"] = {
			["Salt"] = "1 tsp";	
		};
	};
}

I would appreciate any insight on this matter. Note that an alternative solution has already been given by EgoMoose. I just want to find the elusive logic error in my code. Thanks!

2 comments

0

The full code is over 200 lines long and that is why I did not post it all.

0

In that case you can make the loop longer :3

1 answer

✓ Accepted answer

Your problem is this line:

array[indexOfGreatestSoFar] = nil

You're operating on the array part of a Lua table, and the element you are removing (setting to nil) is not the last one, so you're making the array sparse. Neither ipairs nor the # operator are guaranteed to iterate past the first nil value they encounter. In your example, the size of the array as far as # is concerned jumps from 3 to 1.

Because the array you're drawing from is unordered, you can compact on element removal like this:

array[indexOfGreatestSoFar] = array[#array]
array[#array] = nil

4 comments

0

Thank you!

0

Yeah, you just have to keep the array part having consecutive integer indices with no holes. So when you want to remove an element in the middle, you need to either shift all remaining elements down one index to fill the hole (if you need to keep it sorted), or move the last element of the array into the spot your freeing up (cheaper for when you don't need to preserve ordering)

0

I edited my answer to include an example of the latter, the cheap way of compacting an unordered array.

0

I understand now. Thank you so much! I knew that you could not modify a table while looping through it, I just did not know the same was true for the # operator.

This discussion is preserved as it was. New answers, comments, and votes are closed.