Dictionary with ValueTuple as the key

MarcR
VIP

Hi there, 

I want to add data to a dictionary with a key that contains 3 fields. I've been advised to use a ValueTuple but have tried all the syntax options that i could find in Google without success.

 

Ideally it should be like the setup of a databuffer where the key is the databuffercellPk and the value the actual stored number.

My key should contain 3 strings and the value a decimal).

I've tried something like: Dim dctDict as New Dictionary<(String sn, String Tm, String Ac)>, int();

and various other options. 

Does anybody had a suggestion how to create the best performing Dictionary Key that exists of 3 strings?

Marc Roest
OneStream consultant @Finext
1 ACCEPTED SOLUTION

 

Sub Main

	'Initialize collection of data keys
	Dim dkCollection As New Dictionary(Of dataKey, Integer)

	For i = 1 To 12
		Dim dk As New dataKey()
		dk.sn = "Actual"
		dk.tm = $"2020M{i}"
		dk.ac = "Sales"
		dkCollection.Add(dk, 10 * i)
	Next i

	'Generate a new key and serach for it
	Dim searchDk As New dataKey()
	searchDk.sn = "Actual"
	searchDk.tm = "2020M2"
	searchDk.ac = "Sales"

	'Search single key	
	Dim resultDk1 As Integer = dkCollection.Item(searchDk)
	Console.WriteLine($"Single Query Result = {resultDk1}")

	'Search multiple keys
	Dim resultList As List(Of Integer) = dkCollection.Where(Function(x) x.Key.sn = "Actual").Select(Function(y) y.Value).ToList
		Console.WriteLine($"Single Query Result = {String.Join(",", resultList)}")

End Sub

' Define other methods and classes here
' Define other methods and classes here
Private Structure dataKey
	Property sn As String
	Property tm As String
	Property ac As String
End Structure

 

The struct can be generated iteratively, in this case in a for loop. When you search for it you can generate a new struct and if all the property are the same it will match what is in the collection. Essentially the struct itself is just a memory pointer, so you can definitely generate them in a loop. Clearly if you try to add a struct with the same three properties, even if the "name" you give to the object is different, it will return an error, since the name of the object itself exist only in the code you see,  but not in the compiled version the machine is going to run. Essentially it is just a label to avoid you from knowing the memory location of the object. 

Single Query Result = 20
Single Query Result = 10,20,30,40,50,60,70,80,90,100,110,120

View solution in original post

8 REPLIES 8

ChristianW
Valued Contributor

As ugly as it looks, I think, there is no alternative to:

 

 

dim dctDict as new dictionary(of dictionary(of string, string), integer)

dim dctDict as new dictionary(of string, dictionary(of string, integer))

Dim dctDict As New dictionary(Of Tuple(Of String, String), Integer)

 

 

Hi Christian, 

That would also be an issue to get the data in and out of this dictionary. My alternative would be to delimit the items with a pipe and then combine the search fields to this unique key. The only disadvantage is that i cannot search for a specific key (or filter on that). Ideally i would work with it as we do with a datacellPK but i don't see any better solution right now.

 

Marc Roest
OneStream consultant @Finext

ChristianW
Valued Contributor

Did you try this:

 

dctDict3.add(New tuple(Of String, String)("Christian", "Marc"), 100)

brapi.ErrorLog.LogMessage(si, dctDict3(New tuple(Of String, String)("Christian", "Marc")))

 

tsandi
Contributor

What about creating a structure with your three keys and use that as a key for the dictionary? You can then generate a complete search key if you know all the parameters or you can use Linq to query the keys for a single dimension

 

 

Sub Main
	
	'Initialize collection of data keys
	Dim dkCollection As New Dictionary(Of dataKey, Integer)

	'Data key 1
	Dim dk1 As New dataKey()
	dk1.sn = "Actual"
	dk1.tm = "202M1"
	dk1.ac = "Sales"
	dkCollection.Add(dk1, 10)

	'Data key 2
	Dim dk2 As New dataKey()
	dk2.sn = "Actual"
	dk2.tm = "202M2"
	dk2.ac = "Sales"
	dkCollection.Add(dk2, 20)

	'Generate a new key and serach for it
	Dim searchDk As New dataKey()
	searchDk.sn = "Actual"
	searchDk.tm = "202M2"
	searchDk.ac = "Sales"

	'Search single key	
	Dim resultDk1 As Integer = dkCollection.Item(searchDk)

	'Search multiple keys
	Dim resultList As List(Of Integer) = dkCollection.Where(Function(x) x.Key.sn = "Actual").Select(Function(y) y.Value).ToList
	
	
End Sub

' Define other methods and classes here
Private Structure dataKey
	Property sn As String
	Property tm As String
	Property ac As String
End Structure

 

Single Query Result: 20
Multiple Query Results: 10,20

ChristianW
Valued Contributor

This is what the platform is doing (more or less) with the PK classes.

Hi Tommaso,

I will test this one later this week and let you know!

Marc Roest
OneStream consultant @Finext

One additional question, I will add multiple keys, is there a way (next to updating the key) to dim a parameter with a counter? E.g. 

for i = 1 to 3

dim key as “key” & I as datakey

next 

 

I know this example is not working but perhaps there is something to put around “key” & I to make it work.

Marc Roest
OneStream consultant @Finext

 

Sub Main

	'Initialize collection of data keys
	Dim dkCollection As New Dictionary(Of dataKey, Integer)

	For i = 1 To 12
		Dim dk As New dataKey()
		dk.sn = "Actual"
		dk.tm = $"2020M{i}"
		dk.ac = "Sales"
		dkCollection.Add(dk, 10 * i)
	Next i

	'Generate a new key and serach for it
	Dim searchDk As New dataKey()
	searchDk.sn = "Actual"
	searchDk.tm = "2020M2"
	searchDk.ac = "Sales"

	'Search single key	
	Dim resultDk1 As Integer = dkCollection.Item(searchDk)
	Console.WriteLine($"Single Query Result = {resultDk1}")

	'Search multiple keys
	Dim resultList As List(Of Integer) = dkCollection.Where(Function(x) x.Key.sn = "Actual").Select(Function(y) y.Value).ToList
		Console.WriteLine($"Single Query Result = {String.Join(",", resultList)}")

End Sub

' Define other methods and classes here
' Define other methods and classes here
Private Structure dataKey
	Property sn As String
	Property tm As String
	Property ac As String
End Structure

 

The struct can be generated iteratively, in this case in a for loop. When you search for it you can generate a new struct and if all the property are the same it will match what is in the collection. Essentially the struct itself is just a memory pointer, so you can definitely generate them in a loop. Clearly if you try to add a struct with the same three properties, even if the "name" you give to the object is different, it will return an error, since the name of the object itself exist only in the code you see,  but not in the compiled version the machine is going to run. Essentially it is just a label to avoid you from knowing the memory location of the object. 

Single Query Result = 20
Single Query Result = 10,20,30,40,50,60,70,80,90,100,110,120