Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
4

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

Asked by 5 years ago
Edited 5 years ago

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!

0
The full code is over 200 lines long and that is why I did not post it all. User#21908 42 — 5y
0
In that case you can make the loop longer :3 greatneil80 2647 — 5y

Locked by User#19524

This question has been locked to preserve its current state and prevent spam and unwanted comments and answers.

Why was this question closed?

1 answer

Log in to vote
3
Answered by 5 years ago
Edited 5 years ago

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
0
Thank you! User#21908 42 — 5y
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) EmilyBendsSpace 1025 — 5y
0
I edited my answer to include an example of the latter, the cheap way of compacting an unordered array. EmilyBendsSpace 1025 — 5y
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. User#21908 42 — 5y
Ad