A system that allows multiple keys for one value. Introducing KeyLinker

General Use Cases:

Use this when you want to make any number of keys point to a singular value.


How It Works:


Key Linker Source Code HERE

Source Code
local HttpService =  game:GetService("HttpService")

type Key = string | number | instance
type RawKey = string --GUUID

--Private 
local function GarbageColectRefrences(self, RawKey)
	if self.LinkedRefrences[RawKey] then
		for i = #self.LinkedRefrences[RawKey], 1, -1 do
			local Key = self.LinkedRefrences[RawKey][i]
			self.LinkedRefrences[Key] = nil
			table.remove(self.LinkedRefrences[RawKey], i)
		end
	end
end

local KeyLinker = {}
KeyLinker.__index = function(self, Key)
	local RawKey = self.LinkedRefrences[Key]
	return (KeyLinker[Key]) or (RawKey and self[RawKey])
end

KeyLinker.__newindex = function(self, Key, Value)
	local LinkedRefrences = self.LinkedRefrences 

	--Cannot set nil value to nil
	local OldRawKey = LinkedRefrences[Key]
	if (not OldRawKey) and (not Value) then
		return
	end

	if OldRawKey then
		rawset(self, OldRawKey, Value) 

		--Delete it
		if not Value then
			GarbageColectRefrences(self, OldRawKey)
			rawset(self, "Size", math.max(self.Size - 1, 0)) 
		end
		return
	end

	local RawKey = HttpService:GenerateGUID(false)
	LinkedRefrences[RawKey] = {Key}
	LinkedRefrences[Key] = RawKey

	rawset(self, "Size", self.Size + 1) 
	rawset(self, RawKey, Value)
end

function KeyLinker.new()
	return setmetatable({
		LinkedRefrences = {} :: {[Key]: RawKey, [RawKey]: {Key}},
		Size = 0
	}, KeyLinker)
end

function KeyLinker:LinkRefrence(KeyToLinkTo: Key, NewKeys: {Key}) --use to remove refrence aswell
	local LinkedRefrences = self.LinkedRefrences 
	
	local RawKey = LinkedRefrences[KeyToLinkTo]
	
	if RawKey then
		for _, NewKey: Key in NewKeys do
			if not LinkedRefrences[NewKey] and LinkedRefrences[NewKey] ~= RawKey then
				self:UnlinkRefrence(NewKey)
				LinkedRefrences[NewKey] = RawKey
				table.insert(LinkedRefrences[RawKey], NewKey)
			end		
			
		end
	else
		warn(KeyToLinkTo, " is not a valid member of the DataBase")
	end
end

function KeyLinker:UnlinkRefrence(Key: Key)
	local LinkedRefrences = self.LinkedRefrences 

	local RawKey = LinkedRefrences[Key]
	if RawKey then
		local Pos = table.find(LinkedRefrences[RawKey], Key)

		if Pos and Pos == 1 then --If its the only key
			self[Key] = nil 
		else
			if Pos then
				table.remove(LinkedRefrences[RawKey], Pos)
			end
			LinkedRefrences[Key] = nil
		end
	end 
end

return KeyLinker


KeyLinker Methods

--Allows new keys to point to the same value as an original key
LinkRefrence(OriginalKey: any,  NewKeys: {[any]: any}): ()
--Removes any link the given Key had to any value
UnlinkRefrence(KeyToUnlink: any): ()

How to use and Tutorials:

Tutorial & Example 1 | Creating Key Links

In this example, I will show you how to add multiple keys to point to a single value.

So in this example, we are making a database where you want to assign both ProductID and ProductName to a single ProductData value.
Lets assume the product name is: "LightBulbs: and the product ID is: “0x234”.


local KeyLinker = require(game:GetService("ReplicatedStorage").SharedRefrences

local ProductData = KeyLinker.new()

ProductData.LightBulbs = {
	["Price"] = 100
}

ProductData:LinkRefrence("LightBulbs", {"0x234"})
print(ProductData["0x234"].Price) --100

--Lets change the price of this product through the product ID
ProductData["0x234"].Price = 50
print(ProductData.LightBulbs.Price) --50

--Lets change the value of 0x234 to a string
ProductData["0x234"] = "Not Available"
print(ProductData.LightBulbs) --"Not Available"

We have linked both the ProductName “LightBulbs” to the ProductID “0x234”. As you can see, changing the value of the Key: “0x234” also changes the value of the key: “LightBulbs”. This is because they are linked.

Tutorial & Example 2 | Removing Key Links

In this tutorial I will show you how to remove a key link. The key link that is removed will then be able to be used for other links.

Lets say we have a database of roblox players.

DataBase:

Key Player One Player Two
UserID : 10 20
UserName : ChillGuy123 SoloIsTheName321
DisplayName: Chillest One Solo Cord

First lets create this database:

local PlayerDatabase = KeyLinker.new()

PlayerDatabase[10] = {
	Username = "ChillGuy123",
	DisplayName = "Chillest One",
	UserId = 10
}

PlayerDatabase[20] = {
	Username = "SoloIsTheName321",
	DisplayName = "Solo Cord",
	UserId = 20
}

--Link keys so I can access player data via Username, Displayname, or UserId
PlayerDatabase:LinkRefrence(10, {"ChillGuy123", "Chillest One"})
PlayerDatabase:LinkRefrence(20, {"Solo Cord", "SoloIsTheName321"})

Now, what if SoloIsTheName321 wants to change his username to SoloSoCool123

--Change SoloIsTheName321's username in the PlayerDataBase
PlayerDatabase[20].Username = "SoloSoCool123" 
--Now can refrence data with: PlayerDatabase.SoloSoCool123
PlayerDatabase:LinkRefrence(20, {"SoloSoCool123"})

But the problem with this is that SoloIsTheName321 still points to SoloSoCool123 player data. Any assignment of PlayerDatabase.SoloIsTheName321 will still affect the database for SoloCord player data.

Solution: We need to Unlink this key from the database.

--Change SoloIsTheName321's username in the PlayerDataBase
PlayerDatabase[20].Username = "SoloSoCool123" 
--Now can refrence data with: PlayerDatabase.SoloSoCool123
PlayerDatabase:LinkRefrence(20, {"SoloSoCool123"})
PlayerDatabase:UnlinkRefrence(20, {"SoloIsTheName321"})

Now SoloIsTheName321 will no longer point to any value and can now safely be used for new usernames.


FAQ

Question: What if I create a new key link that has the same name as another key link
Answer: The exsisting key link will automatically unlink from it’s original value.

Demonstration
local ProductDataBase = KeyLinker.new()

ProductDataBase["Apple"] = {
	ProductName = "Apple",

	ProductID = "Secret_AppleID",
	Price = 15,
}

ProductDataBase["Orange"] = {
	ProductName = "Orange",
	
	ProductID = "Secret_OrangeID",
	Price = 12,
}

ProductDataBase:LinkRefrence("Apple", {"Secret_AppleID"})
--Secret_AppleID will automatically be unlinked from the Apple key to avoid any issues
ProductDataBase:LinkRefrence("Orange", {"Secret_AppleID", "Secret_OrangeID"}) 

print(ProductDataBase["Secret_AppleID"]) --points to orange data

Question: How to get rid keys/values (Garbage Collecting)
Answer: By simply setting any linked key value to nil, each key link will be removed and the value will be set to nil.

Demonstration
local PlayerDatabase = KeyLinker.new()

PlayerDatabase[10] = {
	Username = "ChillGuy123",
	DisplayName = "Chillest One",
	UserId = 10
}

--Link keys so I can access player data via Username, Displayname, or UserId
PlayerDatabase:LinkRefrence(10, {"ChillGuy123", "Chillest One"})

print(PlayerDatabase["ChillGuy123"]) --prints his data

--Lets delete ChillGuy123 from the database
PlayerDatabase["ChillGuy123"] = nil

print(PlayerDatabase[10]) --nil
print(PlayerDatabase["ChillGuy123"]) --nil
print(PlayerDatabase["Chillest One"]) --nil

Question: What if you remove all key links from a value
Answer: The value will simply be garbage collected

Issue to note
Unfortunatly I do not know how to allow autofill to work with this system. Please help me to find a way to resolve this. As of now this is the only issue.

Let me know what you think (this is my first ever community resource)


Would you use keylinker in your future projects?

  • Yes
  • Im Considering
  • No because I don’t need it
  • No because there are better alternatives

0 voters

The code for the module is at the top of this post

3 Likes

Honestly not sure what I would ever use this for, but It seems pretty well made. Also, reference is spelled as “refrence” instead👍

Thanks for the feedback.

Personally I mainly use this for storing my product database but it can be used for any sort of databse.

With KeyLinker you can access the product data not only through the name of the product but also through the ID or any other refrence you want to assign it.

2 Likes

i’m confused
what’s the difference between this and a regular dictionary

It is spelled as “refrence”, but not written like that. It’s “reference”, in writing terms.

1 Like

Thank you for asking. The primary difference is in the way it behaves.

It allows developers to link keys together. By doing so, different keys can point to the exact same data (memory address).

local PlayerDataBase = KeyLinker.new()

PlayerDataBase.ChoonRaccoon = {
    AccountAge = 365,
    UserName = "ChoonRaccoon",
    DisplayName = "Kylo",
    UserId = 3579980216,
}

PlayerDataBase:LinkRefrence(ChoonRaccoon, {"Kylo", 3579980216})

print(PlayerDataBase.Kylo) --Will print your data
print(PlayerDataBase[3579980216]) --Will print your data
print(PlayerDataBase.ChoonRaccoon)--Will print your data

--It facilitates the process of having multiple keys link to one value, here is some pretty unique behaviour with the module:
PlayerDataBase.Kylo = "Terminated"
print(PlayerDataBase[3579980216]) --Will print "Terminated"
print(PlayerDataBase.ChoonRaccoon) --Will print "Terminated"

--This is because these keys are all linked together. 

I hope you understand this. Please ask me more questions.

2 Likes

I updated the code check it out

This seems pretty handy & useful. Good work

1 Like

After reviewing the source code I did notice some spelling errors though

local function GarbageColectRefrences(self, RawKey)
	if self.LinkedRefrences[RawKey] then
		for i = #self.LinkedRefrences[RawKey], 1, -1 do
			local Key = self.LinkedRefrences[RawKey][i]
			self.LinkedRefrences[Key] = nil
			table.remove(self.LinkedRefrences[RawKey], i)
		end
	end
end

‘GarbageColectRefrences’ → ‘GarbageCollectReferences’
‘Refrence’ → ‘Reference’ (There is lots of these)


Other than that, great work

Anyone having any issues? How can I improve it for you

I don’t like that if you change one key, all of them get changed. But still, great resource.

Thats the whole point of linking keys to a value :sob:
If you want to unlink a key, the module has a function that allows you to do so

1 Like

Hi,I am still sorta confused on why I would need to link multiple keys to one value when i can just access that value through the key i assigned it to? Can you give another example where I would need to do something like this? : )

It’s good for when working with large databases, so for example if you were making a GoFundMe game on roblox, it would be good to have both the store name and store ID link to a single store so users can search for the store through both. On roblox for example, you can search for palyers on roblox either by their Username or UserID. this can be made possible through linking the PlayersAccount to both the UserID and the Username.

Personally, I use this for my shop items in my game. I link both the ProductID and the ProductName of the product to the ProductData so I can find products via both when working with multiple scripts and with devproducts. If you have any questions lmk cause ik i did not explain things right in the documentation.

1 Like

Currently working on a new update, please share me your open-source updates so i can integrate them into the next update