[Closed] Holdling keys and values inside struct
What will be the best solution for managing dictionary data types?
I found two very useful solutions first by Loocas (duber studio) using .net Hashtable and
second by Dave Stewart using mxs Struct. I decide to try struct for this because it is very easy to define many different methods which can not be found in the .net Hashtable. This is “simplified” version of Dave struct.
struct List
(
items = #(),
fn getItems = (this.items),
fn clearList = (this.items = #()),
fn getIdxBy key: val: = if (state = key == unsupplied) and val == unsupplied then 0 else
(
if this.items.count == 0 then 0 else
(
local cnt = 1, idx = 0, word = if state then val else key
while cnt != this.items.count do
(
item = if state then this.items[cnt].val else this.items[cnt].key
if item == word then (idx = cnt ; cnt = this.items.count ; idx) else cnt+=1 ; idx
)
)
),
fn addItem key val = (if (getIdxBy key:key) == 0 do append this.items (dataPair key:key val:val)),
fn addUniqueItem key val = (if ((getIdxBy key:key) == 0 and (getIdxBy val:val) == 0) do append this.items (dataPair key:key val:val)),
fn replaceItemByKey key newKey newVal =
(
if (idx = getIdxBy key:key) != 0 do (this.items[idx].key = newKey ; this.items[idx].val = newVal)
),
fn replaceItemByVal val newKey newVal =
(
if (idx = getIdxBy val:val) != 0 do (this.items[idx].key = newKey ; this.items[idx].val = newVal)
),
fn updateItemVal key newVal =
(
if (idx = getIdxBy key:key) != 0 do this.items[idx].val = newVal
),
fn updateItemKey val newKey =
(
if (idx = getIdxBy val:val) != 0 do this.items[idx].key = newKey
),
fn getItemBy key: val: = if (state = key == unsupplied) and val == unsupplied then undefined else
(
if state then (if (idx = getIdxBy val:val) == 0 then undefined else this.items[idx] )
else (if (idx = getIdxBy key:key) == 0 then undefined else this.items[idx])
),
fn getKey val = (if (idx = getIdxBy val:val) == 0 then undefined else this.items[idx].key),
fn getKeys = (for i = 1 to this.items.count collect this.items[i].key),
fn getVal key = (if (idx = getIdxBy key:key) == 0 then undefined else this.items[idx].val),
fn getVals = (for i = 1 to this.items.count collect this.items[i].val),
fn delItemBy key: val: = if (state = key == unsupplied) and val == unsupplied then print "Supply Key or Value First!" else
(
local idx = (if state then getIdxBy val:val else getIdxBy key:key)
if idx == 0 then print "This Item Is Not In The List" else deleteItem this.items idx
),
fn deleteIndex idx = (if idx > 0 and idx <= this.items.count then deleteItem this.items idx else print "The Index is Out of Range!"),
fn printList = if this.items.count == 0 then print "The List Is Empty!" else
(
for i = 1 to this.items.count do format "%: %
" this.items[i].key this.items[i].val
),
fn sortList type:#key maxtomin:off =
(
fn sortByNameOrCount arr1 arr2 type: maxtomin: =
(
local first, second
case type of (
(#key): (first = arr1.key ; second = arr2.key)
(#val): (first = arr1.val ; second = arr2.val)
)
case of (
(first < second): if not maxtomin then -1 else 1
(first > second): if not maxtomin then 1 else -1
default:0
)
)
qsort this.items sortByNameOrCount type:type maxtomin:maxtomin
this.items
)
)
First problem that I have here is getIdxBy function. Outside the struct works fine but when
I try below example there is a problem.
--example
myList = List()
fn randomVirtualFileGen cnt: viritualPathName:@"C: emp
ew folder est files\" =
(
local sioPath = dotNetClass "System.IO.Path"
outputArr = for i = 1 to cnt collect
(
if viritualPathName == unsupplied then sioPath.GetRandomFileName() --only file name with extension
else viritualPathName + "\\" + sioPath.GetRandomFileName() --ful path file name with extension
)
)
pathsArr = randomVirtualFileGen cnt:10
for i = 1 to pathsArr.count do myList.addUniqueItem ("path_" + i as string) pathsArr[i]
myList.printList()
--result
--#((DataPair key:"path_1" val:"C: emp
ew folder est files\\0cixvjbt.ag2"))
I don’t know why I get only one Item but not 10?
this is overcomplicated version of a struct what i can find follow the link.
give me a couple minutes to make everything really simple…
Thanks for fast respond.
Ok. I will wait.
Yup I always start with complicated solution
here is it… i hope i’ve not missed anything:
struct ListData
(
private
_keys = #(),
_values = #(),
public
fn keyID key = (finditem _keys key),
fn hasKey key = (keyID key > 0),
fn keys = (_keys),
fn values = (_values),
fn addKey key value =
(
if (k = keyID key) == 0 do
(
k = _keys.count + 1
_keys[k] = key
)
_values[k] = value
),
fn getValue key = if (k = keyID key) > 0 do _values[k],
fn setValue key value = if (k = fkeyID key) > 0 do _values[k] = value,
fn removeKey key = if (k = keyID key) > 0 do
(
deleteitem _keys k
deleteitem _values k
)
)
Yup. Seems ok.
You only forget to add print and sort method, but this is more then enough.
For now I yous added “print” method for testing
fn print = if _keys.count == 0 then print "List is empty!" else
(
for i = 1 to _keys.count do format "%: %
" _keys[i] _values[i]
)
And this is the test using above example
myList = ListData()
pathsArr = randomVFGen cnt:10
for i = 1 to pathsArr.count do myList.addKey ("path_" + i as string) pathsArr[i]
myList.print()
--path_1: C: emp
ew folder est files\\rbsk4yf5.wm5
--path_2: C: emp
ew folder est files\\mlo4usai.ph4
--path_3: C: emp
ew folder est files\
iz0cozj.lro
--path_4: C: emp
ew folder est files\\a5bwo0gf.02v
--path_5: C: emp
ew folder est files\\rijg0o3w.ets
--path_6: C: emp
ew folder est files\\zz2tek0a.o2o
--path_7: C: emp
ew folder est files\\dkx0qetk.wba
--path_8: C: emp
ew folder est files\\2oddveli.hqu
--path_9: C: emp
ew folder est files\\3l2htunj.pyj
--path_10: C: emp
ew folder est files\\2ne1ub3d.iu2
OK
Works ok.
print is an extension method… it very specific for different tasks. my structure for general use.
it allows to have any type of keys and any type of values. in general case it cannot be sorted.
you can modify the ListData to make it specific for some type (filename for example)… after that you will be able to sort them, search for equivalent, group by path, etc.
Yup. It’s always better to create this for general usage. That’s why I not tried to use .net Hashtable
mostly because of this and performance issue when you use a lot of data.
This is it, Denis, as always thank you for your time and effort
i would probably use System.Collections.Hashtable in your case (or my own class derived from Hashtable)… i’s much faster than my things shown above
Ok. I belive you.
Can you show example at least some of your struct metods for this collections type or to use Loocas aproch?
As you saw here I need this solution for soring some file paths.
I know that is better to use pathConfig and mapPaths struct but in this case I want to try something different. Also this is a common problem when I try to use file path without “@” sign.
fileSring = "C: emp
ew folder est files est.txt"
-->/*"C: emp
ew folder est files est.txt"*/
fn correctString str =
(
str = substituteString str "
" "\
"
str = substituteString str " " "\ "
)
correctString fileSring
--> "C: emp
ew folder est files est.txt"
Is there a better solution for string correction maybe using .net?
I will do that.Thanks.
Now about your advanced struct.
How do empty _keys and _values arrays data if you are set to be private?
here is more advance version with new methods:
struct ListData
(
private
_keys = #(),
_values = #(),
public
fn keyID key = (finditem _keys key),
fn hasKey key = (keyID key > 0),
fn keys = (_keys),
fn values = (_values),
fn addKey key value overwrite:on ifunique:off =
(
if (k = keyID key) == 0 then
(
if not ifunique or (finditem _values value) == 0 do
(
k = _keys.count + 1
_keys[k] = key
_values[k] = value
)
)
else if overwrite and (not ifunique or (finditem _values value) == 0) do _values[k] = value
),
fn removeKey key = if (k = keyID key) > 0 do
(
deleteitem _keys k
deleteitem _values k
),
fn getValue key = if (k = keyID key) > 0 do _values[k],
fn setValue key value = if (k = fkeyID key) > 0 do _values[k] = value,
fn findKeys value =
(
for k=1 to _values.count where _values[k] == value collect _keys[k]
),
fn makeUniqueValues =
(
local unique = makeUniqueArray _values
_keys = for v in unique collect _keys[finditem _values v]
_values = unique
)
)
first of all let’s clarify ourselves how many filenames we suppose to handle. 10s, 100s, 1000s, 10000s…
if we are talking about hundreds it’s absolutely OK to stay with the mxs only.
so a using anything more than mxs will make everything just more complicated.
we can stay pretty well with memory use and performance. and we be able easily use built-in mxs solutions like the pathConfig structure for example.
I tested tree different methods (mapPath struct, .net Hashtable and ListData struct)
This is a example for testing. Let’s try first to add 500 file names and then find and collect 50 full file names
fn randomVFGen &arr1 &arr2 cnt: viritualPathName:@"C: emp
ew folder est files" =
(
local sioPath = dotNetClass "System.IO.Path"
for i = 1 to cnt do
(
local imgFile = sioPath.GetFileNameWithoutExtension(sioPath.GetRandomFileName()) + ".png"
append arr1 (viritualPathName + "\\" + imgFile)
append arr2 imgFile
)
)
fullPaths = #() ; fileNames = #()
randomVFGen &fullPaths &fileNames cnt:500
fewFiles = for i = 1 to 50 collect fileNames[random 1 500]
Maybe I not test this properly but this is the result
if mapPaths.count() != 0 do for p = mapPaths.count() to 1 by -1 do mapPaths.delete p
fn storeMP arr =
(
local addArr = mapPaths.add
for i in arr do addArr i
)
gffp = mapPaths.getFullFilePath
--ADD
gc() ; t1 = timestamp() ; m1 = heapfree
storeMP fullPaths
format "Adding Method > time:% memory:%
" ((timestamp()- t1 as float)/1000) (m1-heapfree)
--FIND
gc() ; t1 = timestamp() ; m1 = heapfree
for p in fewFiles collect (gffp p)
format "Finding Method > time:% memory:%
" ((timestamp()- t1 as float)/1000) (m1-heapfree)
–RESULT***
–>>> Adding Method > time:102.0 memory:1432L
–>>> Finding Method > time:0.548 memory:5184L
hsh = dotNetObject "System.Collections.Hashtable"
--ADD
gc() ; t1 = timestamp() ; m1 = heapfree
for i in 1 to fileNames.count where not (hsh.ContainsKey fileNames[i]) do hsh.Add fileNames[i] fullPaths[i]
format "Adding Method > time:% memory:%
" ((timestamp()- t1 as float)/1000) (m1-heapfree)
--FIND
gc() ; t1 = timestamp() ; m1 = heapfree
for p in fewFiles collect hsh.item[p]
format "Finding Method > time:% memory:%
" ((timestamp()- t1 as float)/1000) (m1-heapfree)
–RESULT***
–>>> Adding Method > time:0.026 memory:94208L
–>>> Finding Method > time:0.016 memory:9752L
LData = ListData()
--ADD
gc() ; t1 = timestamp() ; m1 = heapfree
for i in 1 to fileNames.count do LData.addKey fileNames[i] fullPaths[i]
format "Adding Method > time:% memory:%
" ((timestamp()- t1 as float)/1000) (m1-heapfree)
--FIND
gc() ; t1 = timestamp() ; m1 = heapfree
for p in fewFiles collect LData.getValue p
format "Finding Method > time:% memory:%
" ((timestamp()- t1 as float)/1000) (m1-heapfree)
–RESULT***
–>>> Adding Method > time:0.017 memory:34352L
–>>> Finding Method > time:0.017 memory:5376L