How to loop through a table with Indexes in order

So I have this table here that I would like to index loop through in order:

local tab = { -- just some randomness but you get the point
	a = "yes",
	sf = 2,
	no = true,
	awa = "efhfdh",
	cush = {"yes"},
	B = 675
}

And I would like to not have them indexed with numbers because I will need some data from this table.

print(tab.a) -- yes

But I also want to loop through this table in order, but I can’t use ipairs() since its not indexed by numbers.

But another problem is using pairs() won’t loop through the table in order:

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

--[[
yes  -  Edit
675  -  Edit
true  -  Edit
▶ {...}  -  Edit
2
efhfdh

^^^ not in order
]]

So I could I loop through a indexed table in order?

Try a for-loop like this:

for idx = 1, #tab do
  print(tab[idx])
end

If you mean in alphabetical order, then you’ll need to sort the table first.

1 Like

that wont work because its not a numbered table.

Dictionaries don’t have an order, instead just use an array with the keys in the order that you want

local order ={"a","sf","no"}-- and so forth

And use a numeric for loop or ipairs to get the key to index with.

To automate this you can look further into using metatables with the __index or __newindex methods in order to create the order array automatically when you add a new key to the table.

Explain more please. I dont really understand

I don’t have much experience with issues such as this, so I’ll give the best method I can:

local dictionary = { -- the dictionary in question to be looped through
   a = 'a', -- example keys and values
   b = 'b',
   d = 'd',
   c = 'c',
   e = 'e'
}

local function loopDictionaryInOrder(dict, func)
  local data = {} -- array to hold the indexes

  for key, value in pairs(dict) do -- loops though the dictionary provided in the first parameter
    table.insert(data, key) -- this adds each key to the 'data' array
  end

  table.sort(data, function(a, b) -- this sorts the data array by each key's byte (numeric representations)
    return tostring(a):byte() < tostring(b):byte()
  end)

  for _, key in ipairs(data) do 
     -- loops through the 'data' array and calls the function provided in the second parameter
     -- the function gives the key as the first argument and the key's value as the second argument
    func(key, dict[key])
  end
end

-- this is an example usage of the function
loopDictionaryInOrder(dictionary, function(key, value)
   -- print each key and value in order
   print(key, value)
end)

this does doesnt work at all and seems to just randomize that table more

table:

local tab = {
	a = "yes",
	sf = 2,
	no = true,
	awa = "efhfdh",
	cush = {"yes"},
	B = 675
}

output:
image

Technically, it’s functioning correctly as B has a lower numeric value than a and therefore appears first.

A possible fix for that:
Try changing this:

return tostring(a):byte() < tostring(b):byte()

To this:

return tostring(a):lower():byte() < tostring(b):lower():byte()

Doesnt work but it does get the first index right:
image

Also heres the code just to make sure we are on the same page:

Wait, I think I’ve misunderstood your original question. Do you want the table to appear in alphabetical order or in the order that the indexes are in?

Whoops, unfortunately, I can’t help then, sorry

I think it depends on if you want the table sorted in order that it is defined or alphabetically.

I’m not at home but I think you would implement what @dthecoolest said like this:

local order = {"a", "sf", "no", "awa", "cush", "B"}  -- This determines the order in which the keys are printed

for idx = 1, #tab do
  print(tab[order[idx]])  -- Or something similar to this
end

This uses the “order” table as an index into “tab”

I’m not familiar enough with metatables to know how to use them in this case.

1 Like

the order the indexes are in not alphabetical lol

I want them in order in the way I created the table. And I need them to have their indexes because I also want to index them somewhere else in the script

The index in the above example would be “idx”…

If you need the index outside of the for-loop, you might need to store the index somewhere like within the same “tab” or even another table…

Maybe using OOP might help organize your data. Or functions to do more within the for-loop without making the loop too big…

Your use-case is vauge because you’ve made your key names abstract, but this should still work fine:

local array = {
    {Name = "a", Something = "yes"},
    {Name = "sf", SomethingElse = 2},
    -- so on and so forth..
}

You can get ordered iteration without losing key-data this way. But this comes with the problem of not being able to directly index without performing some sort of search, but this can be mitigated by using a seperate table (a “map”, or “lookup table” you might say.) to store keys that point to a value in the array. Like this:

local array = {
    {Coolness = 5, Identifier = 10}
}

local map = {
    CoolScale = array[1]
}

However, your mileage is still unclear, so this is likely a redundant and unecessary solution for what you’re trying to acomplish. On that regard, you might benefit from thinking about why you want that table to be strictly in order?

1 Like

My actual reason is because I’m making a plugin with UI settings that I want in order. And to render the UI I just loop through the settings and create a UI element based off that.

But since its a settings table I still need to have a index data. That’s why I need it to be in order and to have key-data.

And I’ll try your method that you provided.

I think this will solve your problem.

2 Likes

Upon revisiting this topic, I now understand that you meant to loop through the dictionary in the order you created it in, which is what I’m also trying to do right now. My only solution for this is to add the key to a separate, indexed table when you add new key-value pairs to your dictionary. Then, you can loop through the indexed table and use the returned key to get the dictionary’s corresponding values.

Usage:

local indexedTable, dictionary = {}, {}

local function addPair(key, value)
    table.insert(indexedTable, key)
    dictionary[key] = value
end

-- To loop, use ipairs
for _, key in ipairs(indexedTable) do
    local dictionaryValue = dictionary[key]
    -- Use value here
end
3 Likes