I accidentally looked at the OrderedDataStore documentation and found my solution, the 2 last properties let you specify the minimum and maximum value you are going to look for in a data store, you can use this to only pick values inside of a specified range.
For cases where Bob has 100000 trophies but you have 100 trophies the range differs too much so it can't be a known value, instead there is a default range value, if no people in that range is found the range is multiplied by 2 and checks again, for example if with numbers 100000 and 100 the base range is 5000, at first it won't find anything, then it will multiply the range by 2 which will give you 10000, still nothing, it will continue multiplying it -> 20000 -> 40000 -> 80000 -> 160000 and when it comes to 160000, 100000 will be in range, this can require a lot of requests to the data store if the difference between users is huge, you can solve that using task.wait, I did not do that since yes.
Here is a module that I wrote for that:
local searchlib = {}
local DataStoreService = game:GetService("DataStoreService")
local data_store
function searchlib.init_data_store(name)
data_store = DataStoreService:GetOrderedDataStore(name)
end
function searchlib.add_count(key, count)
local success, response = pcall(data_store.SetAsync, data_store, key, count)
if not success then
warn("Failed to add count:", response)
end
end
function searchlib.get_count(key)
return data_store:GetAsync(key)
end
function searchlib.get_closest_count(count, required, range)
local closest = {}
local found = 0
while true do
local pages = data_store:GetSortedAsync(false, 50, count - range,
count + range)
while true do
local page = pages:GetCurrentPage()
if #page == 0 then break end
for _, save in pairs(page) do
if closest[save.key] then continue end
closest[save.key] = save.value
found += 1
if found >= required then return closest end
end
if pages.IsFinished then break end
page:AdvanceNextPageAsync()
end
range *= 2
end
return closest
end
return searchlib
And here it is used by a server script for test cases:
local searchlib = require(script.searchlib)
local players = {
["bob"] = 100000,
["me"] = 20,
["yapelize"] = -100,
["feahren"] = 5000,
["fish"] = 8000,
["opalmime"] = 10000,
["weeb"] = 8888888,
["iiilllii"] = 69,
["ruby"] = 1,
["kirda"] = 1013
}
searchlib.init_data_store("test")
for player, trophies in pairs(players) do
searchlib.add_count(player, trophies)
end
print(
"closest for 5000 trophies:", searchlib.get_closest_count(5000, 4, 500),
"closest for 100000 trophies:", searchlib.get_closest_count(100000, 4, 10000),
"closest for -2000 trophies:", searchlib.get_closest_count(-2000, 4, 300)
)
My output after running the code was the following:
closest for 5000 trophies: ? {
["feahren"] = 5000,
["fish"] = 8000,
["kirda"] = 1013,
["opalmime"] = 10000
} closest for 100000 trophies: ? {
["bob"] = 100000,
["feahren"] = 5000,
["fish"] = 8000,
["opalmime"] = 10000
} closest for -2000 trophies: ? {
["iiilllii"] = 69,
["me"] = 20,
["ruby"] = 1,
["yapelize"] = -100
}
The function get_closest_count takes 3 arguments, the first one is amount of trophies you want to get closest matches for, the second is required amount of matches, it will continue looping until it finds this amount of matched opponents and the third is the base range, for bigger numbers it should be possibly bigger since there is less players with a lot of trophies.
init_data_store function initializes the data store with your given name. This module lacks pcalls almost everywhere since that would make it even longer.
If you want to use this with your data store without loosing your data you will need to set async all data from your normal data store into this ordered data store.
4 comments
Wow was it there all the time? This is so useful, never seen it before. And you can do a lot of cool stuff with this
bullying
i donvote!!!!!!12345654312345604306039402 give me more thropuy
also i xapelize!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!3913019019401290940195051515