Ordered Associative Array

Post your working scripts, libraries and tools
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
GitHub: CapnOdin
Location: Denmark

Ordered Associative Array

14 Sep 2017, 17:28

I am sure this already exists on the forum.
Source

Code: Select all

Class OrderedAssociativeArray {
	__New() {
		ObjRawSet(this, "__Data", {})
		ObjRawSet(this, "__Order", [])
	}
	
	__Get(args*) {
		return this.__Data[args*]
	}
	
	__Set(args*) {
		key := args[1]
		val := args.Pop()
		if(args.Length() < 2 && this.__Data.HasKey(key)) {
			this.Delete(key)
		}
		if(!this.__Data.HasKey(key)) {
			this.__Order.Push(key)
		}
		this.__Data[args*] := val
		return val
	}
	
	InsertAt(pos, key, val) {
		this.__Order.InsertAt(pos, key)
		this.__Data[key] := val
	}
	
	RemoveAt(pos) {
		val := this.__Data[this.__Order[pos]]
		this.__Data.Delete(this.__Order[pos])
		this.__Order.RemoveAt(pos)
		return val
	}
	
	Delete(key) {
		for i, v in this.__Order {
			if(key == v) {
				return this.RemoveAt(i)
			}
		}
	}
	
	Length() {
		return this.__Order.Length()
	}
	
	HasKey(key) {
		return this.__Data.HasKey(key)
	}
	
	_NewEnum() {
		return new OrderedAssociativeArray.Enum(this.__Data, this.__Order)
	}
	
	Class Enum {
		__New(Data, Order) {
			this.Data := Data
			this.oEnum := Order._NewEnum()
		}
		
		Next(ByRef key, ByRef val := "") {
			res := this.oEnum.next(, key)
			val := this.Data[key]
			return res
		}
	}
}
Test

Code: Select all

#include OrderedAssociativeArray.ahk
lst := new OrderedAssociativeArray()

lst["x"] := 14
lst["a"] := 1
lst["h"] := 15

test(lst)
lst.Delete("a")
test(lst)
lst.InsertAt(1, "w", 3535)
test(lst)
lst.RemoveAt(2)
test(lst)

test(obj) {
	res := ""
	for i, v in obj {
		res .= i " = " v "`n"
	}
	MsgBox, % res
}
Last edited by Capn Odin on 08 Aug 2021, 05:49, edited 7 times in total.
Please excuse my spelling I am dyslexic.
Guest

Re: Ordered Associative Array

14 Sep 2017, 19:26

Really needed this.
Thanks for sharing :D
User avatar
joedf
Posts: 8545
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: Ordered Associative Array

14 Sep 2017, 20:10

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: Ordered Associative Array

14 Sep 2017, 20:35

@Capn Odin: Thanks for sharing! :thumbup:

I have problems wrapping my head around Next and _NewEnum. Would you mind posting an example use of these? I guess more people other than myself will benefit from those examples.
Maybe you can also offer a sentence or two of explanation, please? Like, why not return this.__Data ?
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Ordered Associative Array

15 Sep 2017, 00:18

Cheers for this. I'll point out some similar code I was working on.

[4 types of array: CSA CSD CIA CID (case (in)sensitive, keys returned in alphabetical/creation date order)]
[not classes as such, but the same principles used to create stand-alone functions]
text/list/table functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 85#p151285
[preliminary (mostly complete) work for classes:]
case sensitive/case insensitive arrays (maintain key order, no key autosort) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=29198
objects: ordered array (function syntax to method syntax) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=31472
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: Ordered Associative Array

15 Sep 2017, 10:33

Hello, thanks for sharing, nice one :thumbup:
Minor comments, you can add __new() and use objrawset for __data and __order to avoid having the ifs in __get and __set. Eg,

Code: Select all

__new(){
	objrawset(this,"__Data",[])
	objrawset(this,"__Order",[])
}
You should adjust your next method, because, "" is a valid key name, and will cause a pretty boring loop ;)
Interesting with insertAt and removeAt methods.

@ wolf_II, hi :wave: _newEnum is (edit: mostly used) for for loops. If __data is returned the for loop will visit the keys in alphabetical order, not in the order in __order.
Cheers.
Last edited by Helgef on 15 Sep 2017, 13:44, edited 1 time in total.
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
GitHub: CapnOdin
Location: Denmark

Re: Ordered Associative Array

15 Sep 2017, 11:01

Added support for nested loops.
Helgef wrote:
Thank you for the suggestions.
wolf_II wrote:
I will explain it after I have had lunch and taken my medication.
Please excuse my spelling I am dyslexic.
arcticir
Posts: 693
Joined: 17 Nov 2013, 11:32

Re: Ordered Associative Array

15 Sep 2017, 11:43

Does it not automatically create an object?
lst["x",3] := 14
lst["x",1] := 14
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
GitHub: CapnOdin
Location: Denmark

Re: Ordered Associative Array

15 Sep 2017, 12:05

arcticir wrote:Does it not automatically create an object?
lst["x",3] := 14
lst["x",1] := 14
Oh, that's right, I had forgotten about this behavior.
Please excuse my spelling I am dyslexic.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Ordered Associative Array

15 Sep 2017, 12:13

Btw, does anyone know if it's possible to have multiple Enum/Next methods, and to include a method that will set which Enum is used? Thanks.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: Ordered Associative Array

15 Sep 2017, 12:18

Ofc jeeswg, newenum can return any object with a next method.
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
GitHub: CapnOdin
Location: Denmark

Re: Ordered Associative Array

15 Sep 2017, 13:10

Changed to use an enum class instead of keeping track of enumeration in the instance itself, this was to add support for empty keys.
Please excuse my spelling I am dyslexic.
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
GitHub: CapnOdin
Location: Denmark

Re: Ordered Associative Array

15 Sep 2017, 17:34

Helgef wrote:
I have definitely needed the functionality this class provides before, but I never felt that making two objects was too much of a hassle. The thing I find most interesting about making this class is to make it feel like a native object.
wolf_II wrote:I have problems wrapping my head around Next and _NewEnum.
Both _NewEnum and Next are used by for loops.
The for loop calls _NewEnum once at the start, it then in order to get the keys and values, in each iteration calls Next on the object returned by _NewEnum. Next is called until it returns False.
wolf_II wrote:Maybe you can also offer a sentence or two of explanation, please? Like, why not return this.__Data ?
The reason for having _NewEnum return something other then __Data is like Helgef said to control the sequence in which the keys and values are feed to the for-loop.
Last edited by Capn Odin on 15 Sep 2017, 18:43, edited 1 time in total.
Please excuse my spelling I am dyslexic.
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
GitHub: CapnOdin
Location: Denmark

Re: Ordered Associative Array

15 Sep 2017, 17:47

arcticir wrote:Does it not automatically create an object?
lst["x",3] := 14
lst["x",1] := 14
It should work now.
Please excuse my spelling I am dyslexic.
arcticir
Posts: 693
Joined: 17 Nov 2013, 11:32

Re: Ordered Associative Array

16 Sep 2017, 01:18

I tested its performance. Compared to the same effect of "OrderedArray ()",It is very slow..
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
GitHub: CapnOdin
Location: Denmark

Re: Ordered Associative Array

16 Sep 2017, 08:51

arcticir wrote:I tested its performance. Compared to the same effect of "OrderedArray ()",It is very slow..
I would love to know what you tested, it will make it easier to figure out where to make improvements.
Were you testing Creating objects, Retrieving values, Adding values, Removing values or Enumeration ?
Please excuse my spelling I am dyslexic.
arcticir
Posts: 693
Joined: 17 Nov 2013, 11:32

Re: Ordered Associative Array

16 Sep 2017, 15:23

Simple "FOR"
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: Ordered Associative Array

16 Sep 2017, 16:17

Maybe try this,

Code: Select all

Class Enum {
	__New(Data, Order) {
		this.Data := Data
		this.orderEnum := Order._NewEnum()
	}
	Next(ByRef key, ByRef val := "") {
		local r, k
		r:=this.orderEnum.next(k,key)
		val:=this.Data[key]
		return r
	}
}
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
GitHub: CapnOdin
Location: Denmark

Re: Ordered Associative Array

16 Sep 2017, 18:17

Helgef wrote:Maybe try this,

Code: Select all

Class Enum {
	__New(Data, Order) {
		this.Data := Data
		this.orderEnum := Order._NewEnum()
	}
	Next(ByRef key, ByRef val := "") {
		local r, k
		r:=this.orderEnum.next(k,key)
		val:=this.Data[key]
		return r
	}
}
Yeah, I came to a similar solution based on this thread linked by jeeswg https://autohotkey.com/boards/viewtopic.php?f=5&t=31472

Code: Select all

Class Enum {
	__New(Data, Order) {
		this.Data := Data
		this.oEnum := Order._NewEnum()
	}
	
	Next(ByRef key, ByRef val := "") {
		res := this.oEnum.next(, key)
		val := this.Data[key]
		return res
	}
}
By the way what do the keyword local do ?

Edit: I made this as my first attempt at optimising it. Do you know why it is slower then the above ?

Code: Select all

Class Enum {
	i := 1
	__New(Data, Order) {
		this.Data := Data
		this.Order := Order
		this.Len := Order.Length() + 1
	}
	
	Next(ByRef key, ByRef val := "") {
		val := this.Data[key := this.Order[this.i]]
		return this.i++ <= this.Len
	}
}
Please excuse my spelling I am dyslexic.
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: Ordered Associative Array

17 Sep 2017, 11:36

Regarding local, it is for avoiding overwritning super-global variables. Ofc, in the examples here there are no such variables, but I put it there due to new habits.
Regarding performance differences, the main differance is probably due to using the built-in enumObject, for loops are in general faster than normal loops when it comes to objects, if irc. I guess it comes down to implementation details, which I know nothing about.
I didn't make any benchmarks, were the differences big between the first version and using the built-in enumerator for the order array?
Maybe a one-liner is even faster: return this.oEnum.next(, key) ? (val := this.Data[key], true) : false. :think:
Finally, maybe you should use the objXXX() functions instead of the corresponding methods, like objHasKey() instead of .hasKey(). As it is now, lst["hasKey"] :=... breaks the script. The functions might be a bit faster too.
Anyways, I think it has turned out very nice and I will refer to it when needed, cheers. :wave:

Return to “Scripts and Functions”

Who is online

Users browsing this forum: jdworkman3245 and 19 guests