Support dictionaries in table.find

Currently table.find does not work with dictionaries. If I have a value and want to find its key in a dictionary, I have to manually iterate over the dictionary and compare the values until one is found. table.find was added to make doing this easier with tables, it should also work with dictionaries

local Values = {"a", "b", "c", "d", "e"}
print(table.find(Values, "d")) -- 4

local Values = {["key1"] = "a", ["key2"] = "b", ["key3"] = "c", ["key4"] = "d", ["key5"] = "e"}
print(table.find(Values, "d")) -- nil, desired value is "key4"
4 Likes

table.find was designed for arrays. I don’t see why table.find should support dictionaries. There is no way to know which occurrence of the value you’re looking for is found.

2 Likes

table.find is used so much, and it might break some scripts by adding this functionality. I still want this feature, so maybe add it a different way?

local data = {
    Equipped1 = "Apple",
    Equipped2 = "Medkit",
    Equipped3 = "Burger",
    Equipped4 = nil,
}
if (method here) and humanoid.Health < 50 then
    print("Maybe you should heal?")
end
-- Methods:
"Medkit" in data
"Medkit" not in data
table.has(data, "Medkit")
table.contains(data, "Medkit")
table.find(table.values(data), "Medkit")
5 Likes

This does make sense because tables don’t have a specified order, but I’m not sure if it matters very often which one you get. They could make it behave like ‘next’ does if we needed to find all of them.

local t= {a = 10, b = 5, c = 10, d = 10}
print(table.find(t, 10)) --> a
print(table.find(t, 10, a)) --> c

But this requires modifications to the parameters and an extension of the current behavior.


I think it may be more beneficial to add a new set of C-based dictionary functions. Honestly I’m surprised we don’t have these already, since the need is so common to count the elements in a dictionary with a crummy Lua for loop.

examples
print(dict.count(t)) --> 4

dict.find(t, 10) --> a

local keys = dict.keys(t)
table.sort(keys)
for key in keys do
    print(dict[key]) --> 10 5 10 10
end

-- we could even get some 'convenience' functions such as these

for key, value in dict.values_sorted(t) do
    print(value) --> 5 10 10 10
end

for key in dict.find_all(t, 10) do
    print(key) --> a c d
end
2 Likes

It’s not even just find, the majority of the table standard library is designed for use with the array portion of tables. Developers have always had to create their own libraries for further dictionary tooling. OP should use open source libraries like sift which supports dictionary tooling.

I don’t think there’s anything wrong with iterating over the dictionary and comparing values or making a reusable parametrised function to avoid repeating the same “manual iteration” code. You’re still just calling a single function, just that it’s not available as a global.

Agree with the above though. If it has to be a thing, would prefer a Roblox standard library or explicit methods that, by design, work with the hash portion of tables. Overriding is strange.

4 Likes

The table library is designed to work with arrays, not dictionaries. Now, I’m not saying that this feature request is invalid in any form, but it would be inconsistent throughout the library’s design. Additionally, what if something like this exists:

local myUnorganizedTableThing = {
StringIndex = workspace.Bighead2,
[646511225] = "i_am_admin",
Vector3.new(4,7,78) = workspace.SpawnLocation,
65,
"tablestuff",
[4] = 'i am not replacing 4th index right?',
[print] = {"i print for you, don't forget about me. im low on ink btw", InkLevel = .094}
}

table.find just wouldn’t work here. It would break a lot of code if it suddenly started returning functions stored as the index in these abominations unique tables.

However, if we forget about backwards compatibility and consistency for a second, it does seem like a good addition. It should be implemented through a different library or a function of a different name. It would technically be the reverse of the __index metamethod, which we should also get.

1 Like

It would be a nice feature but there are other options out there as @colbert2677 said. The table library is also primarily for arrays so it wouldn’t make sense to have a dictionary-specific function in there.

If you’re talking about having one function that can loop through both then it’s just easier to use an OSS tool.

Personally, I do not see why the table module meant for tables should have arbitrary support for dictionaries when the solution to finding keys is as easy as defining a function.

Although yes, defining it all the time is annoying and it has flaws (such as only working with dictionaries that have unique values… which is rare), so I do totally agree that there should be a way to find the keys from a dictionary value.

local function find<a>(haystack: {[any]: a}, needle: a)
    if haystack[needle] then
        return haystack[needle]
    end
    return nil
end
1 Like
  1. If a value does not exist in a table, it will be nil, so the code can be shortened to:
local function find<a>(haystack: {[any]: a}, needle: a)
    return haystack[needle]
end
  1. You don’t need a function for this, it will only made code slower and harder to read:
local A = find(MyTable, MyKey) -- Bad

-- vs

local A = MyTable[MyKey] -- Good
  1. This is not what the feature request is asking for. You are effectively saying that table.find is just Haystack[Needle], which it quite obviously isn’t. table.find is actually:
function table.find(Haystack, Needle)
    for NeedleIndex, PossibleNeedle in Haystack do
        if PossibleNeedle == Needle then
            return NeedleIndex
        end
    end
    
    return nil
end

(NOTE: table.find in implemented in C++ and is not programmed exactly like that, but they give the same result.)

So, what the feature request is asking for is the above to work for dictionaries:

-- This is an array

local Sites = {
    "devforum.roblox.com",
    "create.roblox.com",
    "www.roblox.com"
}

-- To get "create.roblox.com", you do this:
local CreatorSite = Sites[2]
-- Sites[2] gets the second entry in the array

-- This is a dictionary

local Sites = {
    DevForum = "devforum.roblox.com",
    CreatorSite = "create.roblox.com",
    MainSite = "www.roblox.com"
}

-- Now, to get create.roblox.com you do this instead:
local CreatorSite = Sites.CreatorSite
-- Sites.CreatorSite looks for the entry in the dictionary with "CreatorSite"
-- before it, which is it's "key".

Because, table.find doesn’t work for dictionaries, and this feature request is requesting that it is updated so it can.