The last AutoHotkey (AHK) array class you will ever need.
- Introduction
- AutoHotkey link
- Quick start
- Decide which sort type to use
- Set the sort type
- Set ContainerObj.CallbackValue
- Set ContainerObj.CallbackCompare
- Use the object - Introduction
- Use the object - Sort the container
- Use the object - Binary search
- Use the object - The Value parameter
- Use the object - More on binary search
- Quick start summary
- Sort type
- CONTAINER_SORTTYPE_CB_DATE
- CONTAINER_SORTTYPE_CB_DATESTR
- CONTAINER_SORTTYPE_CB_NUMBER
- CONTAINER_SORTTYPE_CB_STRING
- CONTAINER_SORTTYPE_CB_STRINGPTR
- CONTAINER_SORTTYPE_DATE
- CONTAINER_SORTTYPE_DATESTR
- CONTAINER_SORTTYPE_DATEVALUE
- CONTAINER_SORTTYPE_MISC
- CONTAINER_SORTTYPE_NUMBER
- CONTAINER_SORTTYPE_STRING
- CONTAINER_SORTTYPE_STRINGPTR
- Instantiating a Container
- Instantiating a Container - Static methods
- Instantiating a Container - Instance methods
- Container.Prototype.Copy
- Container.Prototype.DeepClone
- Container.FromArray
- Container.StrSplit
- Comparing strings
- Comparing numbers
- Comparing dates
- Custom comparisons
- Iterative methods
- Class details
- Static methods
- Static properties
- Instance methods - Alphabetized list
- Instance methods - Categorized list
- Instance methods - Sort methods
- Instance methods - Binary search methods
- Instance methods - Find methods
- Instance methods - Insert methods
- Instance methods - Delete methods
- Instance methods - Remove methods
- Instance methods - Date methods
- Instance methods - Instantiation methods
- Instance methods - Iterative methods
- Instance methods - General methods
- Instance properties - Alphabetized list
- Instance properties - Categorized list
- Miscellaneous info
- Changelog
- Table of contents generated by Headers2ToC.ahk
Note that in this documentation an instance of Container is referred to either as "a Container
object" or ContainerObj.
class Container extends ArrayContainer inherits from Array and exposes over 100 additional methods to perform common actions
such as sorting and finding values.
The class methods can be divided into three categories:
- Methods that sort the values in the container. - sort methods
- Methods that require the container to be sorted - binary search methods.
- Methods that do not require the container to be sorted - iterative methods and general methods.
Categories 2 and 3 above can be further divided into two subcategories:
- Methods that allow the container to have unset indices.
- Methods that do not allow the container to have unset indices.
The sorting methods always require all indices to have a value. Use Container.Prototype.Condense
to remove unset indices.
Join the discussion on autohotkey.com.
This section provides the minimum information needed to work with the class' sorting and binary search methods. You can run the examples in this section from file test\test-readme-examples.ahk.
Using Container objects' sort and binary search methods requires the property ContainerObj.SortType
to be set with a valid integer. Set the sort type by calling Container.Prototype.SetSortType, or
any of the static macros (e.g. Container.CbString) or instance macros (e.g. Container.Prototype.ToCbString).
c := Container(
{ Name: "obj1" }
, { Name: "obj3" }
, { Name: "obj2" }
, { Name: "obj5" }
)
c.SetSortType(If your editor supports jsdoc-style parameter hints,
press your keyboard shortcut for parameter hints after the open parenthesis to
view detailed information about the sort types. You can also find this information in the code
file directly above the method Container.Prototype.SetSortType, or in this readme in section
Sort type.
Finish the method call using the global variable. I would recommend using the global variables instead
of hardcoding the values because it is more readable. If your code encounters a var unset error, call
Container_SetConstants() before using the variables.
; ... continuing with our example
c.SetSortType(CONTAINER_SORTTYPE_CB_STRING)If your container contains references to objects, then you will need to define the callback function that returns the value that will be used for sort and find operations. If your container does not contain references to objects, you can still define a callback to convert the values into something else more useful for sorting (if the values are not inherently sortable).
Skip this step if the values in the container are to be sorted as they are.
In our example above we set the sort type to CONTAINER_SORTTYPE_CB_STRING. The "CB" stands for
"callback". The sort types with "CB" in the symbol direct the relevant methods to call the callback
to get the sort value of the items in the container.
; ... continuing with our example
c.SetCallbackValue(CallbackValue)
CallbackValue(value) {
return value.Name
}In most cases your code will need to set property ContainerObj.CallbackCompare. The three exceptions
are CONTAINER_SORTTYPE_CB_NUMBER, CONTAINER_SORTTYPE_DATEVALUE, and CONTAINER_SORTTYPE_NUMBER,
which compare values using subtraction.
- If the values will be compared as strings, call
Container.Prototype.SetCompareStringEx. Your code can call it without any parameters if the default values are acceptable. A common flag you might want to use isLINGUISTIC_IGNORECASE, which you can pass as the global variable. If your code encounters a var unset error, callContainer_SetConstants()before using the variables.; ... continuing with our example c.SetCompareStringEx(, LINGUISTIC_IGNORECASE) - If the values will be compared as date strings, call one of
Container.Prototype.SetCompareDate,Container.Prototype.SetCompareDateStr,Container.Prototype.SetDateParser, orContainer.Prototype.DatePreprocess. See the section Comparing dates for more information. - Define your own function to use custom logic.
- Parameters:
- A value to be compared.
- A value to be compared.
- Returns:
- A number less than zero to indicate the first parameter is less than the second parameter.
- Zero to indicate the two parameters are equal.
- A number greater than zero to indicate the first parameter is greater than the second parameter.
- Example:
At the top of the description of most instance methods is a line that says "Requires a sorted container: yes/no" and a line that says "Allows unset indices: yes/no".
Methods that require a sorted container are methods that implement a binary search. A binary search is when you split a range in half repeatedly to narrow in on an input value.
Most methods that do not require a sorted container are methods that iterate over items in the container sequentially. These are adapted from javascript array methods.
Methods that allow unset indices are designed to check whether an index has a value before performing
the action on that index. These methods typically have the word "Sparse" at the end of the method name,
e.g. Container.Prototype.FindSparse.
There are three sort methods available:
Container.Prototype.InsertionSort- Sorts in-place and is appropriate for small containers (n <= 32).c.InsertionSort()
Container.Prototype.Sort- Sorts in-place (heap sort) and is appropriate for all containers.c.Sort()Container.Prototype.QuickSort- Does not mutate the original container (returns a new container) and is about 30% faster thanContainer.Prototype.Sort, but uses up to 10x the memory. I recommend usingContainer.Prototype.QuickSortin any case where sorting in-place is not necessary and where memory is not an issue. You can return the value to the same variable; the returned container will have any own properties from the original, including properties added by external code, and will have the same base as the original:c := c.QuickSort()
Binary search requires a sorted container. There are over twenty binary search methods available, including actions such as finding, deleting, and inserting values in order. They all rely on the various "Find" methods. The following is a brief description of the "Find" methods available:
- Find : Finds a value and returns the index.
- FindAll : Finds a value and returns the first index with the value and has a
VarRefparameter that will receive the last index with the value. - FindInequality : Finds the first value that is >, >=, <, or <= the input value, and returns the index.
Container also has a sparse version of each of the above.
Understand that, if your code will be using the binary search methods, the container must stay
sorted. Your code should not add values to the container with Array.Prototype.Push or
Container.Prototype.PushEx. Instead, use any of the "Insert" methods
to insert values in order.
To find the index where a value is located, use the "Find" methods. Continuing with our earlier example...
index := c.Find("obj1")
OutputDebug(index "`n") ; 1
index := c.Find("obj4")
OutputDebug(index "`n") ; 0
index := c.Find(c[3])
OutputDebug(index "`n") ; 3To get a value as a return value, use "GetValue" or "GetValueSparse".
if ObjPtr(c.GetValue("obj2")) == ObjPtr(c[2]) {
OutputDebug("obj2 is in the correct position.`n")
}To insert a value in order, use one of the "Insert" methods.
c.Insert({ Name: "obj4" })
index := c.Find("obj4")
OutputDebug(index "`n") ; 4To delete a value and leave an unset index, use one of the "DeleteValue" methods.
index := c.DeleteValue("obj4")
OutputDebug(index "`n") ; 4
OutputDebug(c.Has(4) "`n") ; 0To remove a value and shift values to the left to fill in the space, use one of the "Remove" methods.
index := c.Remove("obj3")
OutputDebug(index "`n") ; 3
; The empty index from deleting "obj4" is now at index 3
OutputDebug(c.Has(3) "`n") ; 0
; "obj5" is now at index 4
OutputDebug(c[4].Name "`n") ; obj5You will notice that the first parameter of any method that implements a binary search
is Value - the value to find.
The type of value that is valid to pass to Value depends on the sort type, but can be appropriately
summarized as:
- If
ContainerObj.CallbackValuehas been set,Valuecan be an object as long as theCallbackValuefunction can be used to return the sort value. Valuecan also be a primitive value as long as the value is valid for the sort type.
Each of the following are valid using our example container:
val := "obj1"
c.Find(val)val := { Name: "obj1" }
c.Find(val)val := c[1]
c.Find(val)Date values behave somewhat differently because there are two kinds of date strings that can be
recognized by Container - standard yyyyMMddHHmmss strings, and also strings that will be passed
to an instance of Container_DateParser. Furthermore, Container.Prototype.DatePreprocess converts
date strings to a number, expanding the permissible kinds of values to include integers repesenting
date values (number of seconds since Jan 01, 1, 00:00:00).
See Comparing dates for more information.
Here is a comprehensive list:
CONTAINER_SORTTYPE_CB_DATE:- Object : A value that, when passed to
ContainerObj.CallbackValue, returns a yyyyMMddHHmmss string or integer. - Primitive : A yyyyMMddHHmmss string or integer.
- Object : A value that, when passed to
CONTAINER_SORTTYPE_CB_DATESTR:- Object : A value that, when passed to
ContainerObj.CallbackValue, returns a date string that can be parsed byContainerObj.DateParser. - Primitive : A yyyyMMddHHmmss string or integer.
- Primitive : A date string that can be parsed by
ContainerObj.DateParser.
- Object : A value that, when passed to
CONTAINER_SORTTYPE_CB_NUMBER:- Object : A value that, when passed to
ContainerObj.CallbackValue, returns a number or numeric string. - Primitive : A number or numeric string.
- Object : A value that, when passed to
CONTAINER_SORTTYPE_CB_STRING:- Object : A value that, when passed to
ContainerObj.CallbackValue, returns a string. - Primitive : An integer representing the ptr to a null-terminated string.
- Primitive : A string value.
- Object : A value that, when passed to
CONTAINER_SORTTYPE_CB_STRINGPTR:- Object : A value that, when passed to
ContainerObj.CallbackValue, returns an integer representing the ptr to a null-terminated string. - Primitive : An integer representing the ptr to a null-terminated string.
- Primitive : A string value.
- Object : A value that, when passed to
CONTAINER_SORTTYPE_DATE:- Primitive : A yyyyMMddHHmmss string or integer.
CONTAINER_SORTTYPE_DATESTR:- Primitive : A yyyyMMddHHmmss string or integer.
- Primitive : A date string that can be parsed by
ContainerObj.DateParser.
CONTAINER_SORTTYPE_DATEVALUE:- Object : A value that, when passed to
ContainerObj.CallbackValue, returns a date string that can be parsed byContainerObj.DateParser. - Object : A value that was an item in the container when
Container.Prototype.DatePreprocessorContainer.Prototype.DateUpdatewas called, and that still has the property set with the date value as integer. - Primitive : A date string that can be parsed by
ContainerObj.DateParser. - Primitive : An integer representing the number of seconds between Jan 01, 1, 00:00:00 and the date.
- Object : A value that, when passed to
CONTAINER_SORTTYPE_MISC:- Object or primitive : Any value that can be passed to
ContainerObj.CallbackCompare.
- Object or primitive : Any value that can be passed to
CONTAINER_SORTTYPE_NUMBER:- Primitive : A number.
CONTAINER_SORTTYPE_STRING:- Primitive : An integer representing the ptr to a null-terminated string.
- Primitive : A string value.
CONTAINER_SORTTYPE_STRINGPTR:- Primitive : An integer representing the ptr to a null-terminated string.
- Primitive : A string value.
Internally, AutoHotkey's Map class and object property tables implement a binary search.
This allows us to associate values with string names.
Though Map is sufficient for many cases, I often found myself wanting the best of both worlds - I
want to be able to refer to an item by name, and I also want to be able to refer to an item by index
and use operations that are dependent on the values being serialized. I wrote Container for this
use case.
Our example container is already set up to be used this way, but let's recreate it for demonstration:
; Items must have a property that can be used as the name / key.
c := Container(
{ Name: "obj1" }
, { Name: "obj3" }
, { Name: "obj2" }
, { Name: "obj5" }
)
; Set the sort type to `CONTAINER_SORTTYPE_CB_STRING`.
c.SetSortType(CONTAINER_SORTTYPE_CB_STRING)
; Set CallbackValue with a function that returns the name / key.
c.SetCallbackValue(CallbackValue)
CallbackValue(value) {
return value.Name
}
; Call `Container.Prototype.SetCompareStringEx` with / without optional parameters.
c.SetCompareStringEx(, LINGUISTIC_IGNORECASE)
; Sort the container.
c := c.QuickSort()Using the CONTAINER_SORTTYPE_CB_STRING sort type allows us to define an object property as the
source of the name. As long as each object in the container has the same property that returns
a string value, then we can use the container in a manner similar to a Map object.
The following is a list of methods that are analagous to Map instance methods.
Map.Prototype.Clear-- Use
ContainerObj.Length := 0.
- Use
Map.Prototype.Clone-- Use
Array.Prototype.Clone(i.e. callContainerObj.Clone()) to include the items in the clone - Use
Container.Prototype.Copyto only copy the own properties and base object. - Use
Container.Prototype.DeepCloneto deep clone the own properties and items and copy the base object.
- Use
Map.Prototype.Delete-Map.Prototype.Get-- Use find methods.
if c.Find("obj3", &value) { ; do something, probably with `value` }
obj := c.GetValue("obj1")
- Use find methods.
Map.Prototype.Has-- Use find methods.
if c.Find("obj3") { ; do something }
- Use find methods.
Map.Prototype.Set-- Use insert methods.
c.Insert({ Name: "obj4" })
if c.InsertIfAbsent({ Name: "obj1" }) { ; do something }
- Use insert methods.
Map.Prototype.__Enum-- Use
Container.Prototype.Enum,Container.Prototype.EnumRange, orContainer.Prototype.EnumRangeSparse.for index, name, obj in c.EnumRange(3, "obj2", "obj4") { OutputDebug(index ": " name " - " Type(obj) "`n") }
- Use
- A
Containerobject requires 1-3 properties to be viable for sort and binary search operations.ContainerObj.SortType- An integer representing the sort type.ContainerObj.CallbackCompare- A function which compares two values.- For string comparisons, call
Container.Prototype.SetCompareStringEx. - For date comparisons with yyyyMMddHHmmss strings, call
Container.Prototype.SetCompareDate. - For date comparisons with date strings, call
Container.Prototype.SetCompareDateStrwith a format string. - Number comparisons do not require a comparator.
- For string comparisons, call
ContainerObj.CallbackValue- A function that returns the sort value, mostly used with objects to return a property value.
- To use a
Containerlike aMapobject, use sort typeCONTAINER_SORTTYPE_CB_STRING. - Add values to the container using one of the "Insert" methods to keep the values in order.
- Methods are divided into sparse and non-sparse versions.
- Iterative methods do not require any preparation; they can always be called.
Don't forget to leave a ⭐ if you think Container is pretty awesome!
This section lists the sort types, which properties they require, and provides an example of each.
- Jump to:
- CONTAINER_SORTTYPE_CB_DATE
- CONTAINER_SORTTYPE_CB_DATESTR
- CONTAINER_SORTTYPE_CB_NUMBER
- CONTAINER_SORTTYPE_CB_STRING
- CONTAINER_SORTTYPE_CB_STRINGPTR
- CONTAINER_SORTTYPE_DATE
- CONTAINER_SORTTYPE_DATESTR
- CONTAINER_SORTTYPE_DATEVALUE
- CONTAINER_SORTTYPE_MISC
- CONTAINER_SORTTYPE_NUMBER
- CONTAINER_SORTTYPE_STRING
- CONTAINER_SORTTYPE_STRINGPTR
- CallbackValue: Provided by your code and returns a string in the format yyyyMMddHHmmss.
- CallbackCompare: Set by calling
Container.Prototype.SetCompareDate.
CallbackValue(value) {
return value.timestamp
}
c := Container(
{ timestamp: "20250312122930" }
, { timestamp: "20250411122900" }
, { timestamp: "20251015091805" }
)
c.SetSortType(CONTAINER_SORTTYPE_CB_DATE)
c.SetCallbackValue(CallbackValue)
c.SetCompareDate()
c.Sort()- CallbackValue: Provided by your code and returns a date string in any format recognized by the
Container_DateParserset toContainerObj.DateParser. - CallbackCompare: Set by calling
Container.Prototype.SetCompareDateStrorContainer.Prototype.SetDateParser.
CallbackValue(value) {
return value.date
}
c := Container(
{ date: "2025-03-12 12:29:30" }
, { date: "2025-04-11 12:29:00" }
, { date: "2025-10-15 09:18:05" }
)
c.SetSortType(CONTAINER_SORTTYPE_CB_DATESTR)
c.SetCallbackValue(CallbackValue)
c.SetCompareDateStr("yyyy-MM-dd HH:mm:ss")
c.Sort()- CallbackValue: Provided by your code and returns a number.
- CallbackCompare: Not used.
CallbackValue(value) {
return value.value
}
c := Container(
{ value: 298581 }
, { value: 195801 }
, { value: 585929 }
)
c.SetSortType(CONTAINER_SORTTYPE_CB_NUMBER)
c.SetCallbackValue(CallbackValue)
c.Sort()- CallbackValue: Provided by your code and returns a string.
- CallbackCompare: Set by calling
Container.Prototype.SetCompareStringEx.
CallbackValue(value) {
return value.name
}
c := Container(
{ name: "obj4" }
, { name: "obj3" }
, { name: "obj1" }
)
c.SetSortType(CONTAINER_SORTTYPE_CB_STRING)
c.SetCallbackValue(CallbackValue)
c.SetCompareStringEx()
c.Sort()If you know your code will be used for a lot of sorting and finding operations, you can improve performance by storing the name / key in a buffer.
- CallbackValue: Provided by your code and returns a pointer to a null-terminated string.
- CallbackCompare: Set by calling
Container.Prototype.SetCompareStringEx.
class ImageSamples {
__New(Name, ImageData) {
this.NameBuffer := Buffer(StrPut(Name, "cp1200"))
StrPut(Name, this.NameBuffer, "cp1200")
this.ImageData := ImageData
}
Name => StrGet(this.NameBuffer, "cp1200")
}
CallbackValue(value) {
return value.NameBuffer.Ptr
}
c := Container(
ImageSamples("obj4", data4)
, ImageSamples("obj3", data3)
, ImageSamples("obj1", data1)
)
c.SetSortType(CONTAINER_SORTTYPE_CB_STRINGPTR)
c.SetCallbackValue(CallbackValue)
c.SetCompareStringEx()
c.Sort()- CallbackValue: Not used. Values in the container are date strings in the format yyyyMMddHHmmss.
- CallbackCompare: Set by calling
Container.Prototype.SetCompareDate.
c := Container(
"20250312122930"
, "20250411122900"
, "20251015091805"
)
c.SetSortType(CONTAINER_SORTTYPE_DATE)
c.SetCompareDate()
c.Sort()- CallbackValue: Not used. Values in the container are date strings in any format recognized by the
Container_DateParserset toContainerObj.DateParser. - CallbackCompare: Set by calling
Container.Prototype.SetCompareDateStrorContainer.Prototype.SetDateParser.
c := Container(
"2025-03-12 12:29:30"
, "2025-04-11 12:29:00"
, "2025-10-15 09:18:05"
)
c.SetSortType(CONTAINER_SORTTYPE_DATESTR)
c.SetCompareDateStr("yyyy-MM-dd HH:mm:ss")
c.Sort()Your code does not assign this sort type directly. See the description for Container.Prototype.DatePreprocess
for details about this sort type.
- CallbackValue: Not used. Values in the container are any kind of value.
- CallbackCompare: Provided by your code and implements custom logic to return the comparison value.
; Sort words by string length
CallbackCompare(value1, value2) {
return StrLen(value1) - StrLen(value2)
}
c := Container(
"cat"
, "elephant"
, "kale"
)
c.SetSortType(CONTAINER_SORTTYPE_MISC)
c.SetCallbackCompare(CallbackCompare)- CallbackValue: Not used. Values in the container are numbers.
- CallbackCompare: Not used.
c := Container(
298581
, 195801
, 585929
)
c.SetSortType(CONTAINER_SORTTYPE_NUMBER)
c.Sort()- CallbackValue: Not used. Values in the container are strings.
- CallbackCompare: Set by calling
Container.Prototype.SetCompareStringEx.
c := Container(
"string4"
, "string3"
, "string1"
)
c.SetSortType(CONTAINER_SORTTYPE_STRING)
c.SetCompareStringEx()
c.Sort()- CallbackValue: Not used. Values in the container are pointers to null-terminated strings.
- CallbackCompare: Set by calling
Container.Prototype.SetCompareStringEx.
StrBuf(str) {
buf := Buffer(StrPut(str, "cp1200"))
StrPut(str, buf, "cp1200")
return buf
}
buf1 := StrBuf("string4")
buf2 := StrBuf("string3")
buf3 := StrBuf("string1")
c := Container(
buf1.Ptr
, buf2.Ptr
, buf3.Ptr
)
c.SetSortType(CONTAINER_SORTTYPE_STRINGPTR)
c.SetCompareStringEx()
c.Sort()The examples in the Quick start section demonstrate how to prepare a container the long way so you can understand the various components. Typically you will instantiate a container using an alternative method.
Use one of the static methods to instantiate a new container with all the needed properties for the specified sort type.
Container.CbDateContainer.CbDateStrContainer.CbDateStrFromParserContainer.CbNumberContainer.CbStringContainer.CbStringPtrContainer.DateContainer.DateStrContainer.DateStrFromParserContainer.DateValueContainer.MiscContainer.NumberContainer.StringContainer.StringPtr
Use one of the "To<Name>" instance methods to set the needed properties on an existing instance.
Container.Prototype.ToCbDateContainer.Prototype.ToCbDateStrContainer.Prototype.ToCbDateStrFromParserContainer.Prototype.ToCbNumberContainer.Prototype.ToCbStringContainer.Prototype.ToCbStringPtrContainer.Prototype.ToDateContainer.Prototype.ToDateStrContainer.Prototype.ToDateStrFromParserContainer.Prototype.ToDateValueContainer.Prototype.ToMiscContainer.Prototype.ToNumberContainer.Prototype.ToStringContainer.Prototype.ToStringPtr
Container.Prototype.Copy allows you to use one Container object to instantiate another identical
Container object. Unlike Array.Prototype.Clone which will also copy the items, Container.Prototype.Copy
only copies the own properties and the base object to the new instance. If your project will be using
a number of Container objects with the same setup, you can simply create a template and then any time
your project needs a new instance, copy the template.
Make the template:
template_calldate := Container.DateValue(Container_CallbackValue_Calldate, "yyyy-MM-dd HH:mm:ss")
Container_CallbackValue_Calldate(value) {
return value.calldate
}Make a copy:
c := template_calldate.Copy()Any own properties which were added to template_calldate will be reflected on c, and also if the
base of template_calldate was changed then c will have the same base (assuming the base still
inherits from Container and does not override Container.Prototype.Copy).
Container.Prototype.Copy does not deep clone property values that are objects. For object
property values, the value on the template will be the same object as the one on the new instance.
Container.Prototype.DeepClone is adapted from my
Object.Prototype.DeepClone.
It will deep clone the Container object, its own properties, and its items. The return value is a
Container object with the same base, deep cloned own properties, and deep cloned items. It does
not deep clone inherited property values that are objects.
Container.FromArray takes an existing Array object and converts it to a Container object, returning
the original object after changing the base to Container.Prototype.
Container.StrSplit calls AutoHotkey's built-in StrSplit,
then sets the base of the array to Container.Prototype, effectively allowing your code to instantiate
a Container object using StrSplit.
Strings are compared using
Microsoft's CompareStringEx.
When sorting values using string comparison, your code will call Container.Prototype.SetCompareStringEx
or one of the instantiation helpers which calls
Container.Prototype.SetCompareStringEx.
NlsVersionInfoEx is a class used to create an
NLSVERSIONINFOEX
structure. This is passed to Container.Prototype.SetCompareStringEx to specify the National Language
Support version. For general use cases you can leave the NlsVersionInfo parameter as the default
(0). The NLS version is only needed if your code is working with older datasets or libraries that
expect a specific version with respect to sort and comparison operations.
Numbers are compared with basic subtraction.
CompareNumbers(value1, value2) {
return value1 - value2
}This section details how Container handles yyyyMMddHHmmss timestamps, date strings, and date values.
Much of this information is available in the parameter hints for the relevant methods.
Note that there is no built-in support for dates prior to year 1, nor support for date systems other than the modern proleptic Gregorian calendar.
The methods Container.Prototype.SetCompareDateStr and Container.Prototype.SetDateParser, have
a parameter UseCompareDateEx which specifies how date values will be compared. (Note that any
of the instantiation helpers that instantiate a Container object with
a date-related sort type also exposes UseCompareDateEx).
When UseCompareDateEx is false, date values are compared using AutoHotkey's built-in DateDiff.
When a Container_Date object is used, the method Container_Date.Prototype.Diff facilitates
this process.
I added UseCompareDateEx to address this limitation of DateDiff - "If DateTime contains an
invalid timestamp or a year prior to 1601, a ValueError is thrown.
When UseCompareDateEx is true, date strings are compared using a "date value", which is an integer
representing the number of seconds between Jan 01, 1, 00:00:00 and the date associated with the
Container_Date object. The value is returned by the dynamic property
Container_Date.Prototype.TotalSeconds which calls
Container_Date.GetSeconds("00010101000000", this.Timestamp). A couple things to note:
- This process is noticeably slower than
DateDiff, but this is mitigated by callingContainer.Prototype.DatePreprocesswhich optimizes date comparisons significantly. - Leap years are handled using the following logic which I obtained
from Wikipedia.
- Leap years occur every 4 years (Mod(year, 4) = 0) except years that are divisible by 100 and not divisible by 400 are not leap years.
- This logic is boiled down to the following function to return the number of leap years prior to
an input year:
static LeapCountBefore(Year) { k := Year - 1 return Floor(k / 4) - Floor(k / 100) + Floor(k / 400) }
Container_Date and Container_DateParser
are repurposed from my DateObj class.
The methods are fully documented in either file, but this section provides a quick rundown.
Container_Date.FromTimestamp accepts a whole or partial timestamp (yyyyMMddHHmmss) string / integer
and returns a Container_Date object. Container_Date.FromTimestamp is used with sort type
CONTAINER_SORTTYPE_CB_DATE and CONTAINER_SORTTYPE_DATE when UseCompareDateEx is true.
Container_DateParser is a regex-based system for parsing dates from arbitray text. To use it, your
code provides a date format string that the parser can use to identify dates in some input text.
c := Container(
{ calldate: "2025-03-01 12:13:01" }
, { calldate: "2025-03-01 12:24:15" }
, { calldate: "2025-03-01 12:15:09" }
, { calldate: "2025-03-01 12:30:30" }
)
c.SetSortType(CONTAINER_SORTTYPE_CB_DATESTR)
; When we call `Container.Prototype.SetCompareDateStr` we include the date format
c.SetCompareDateStr("yyyy-MM-dd HH:mm:ss")
c.SetCallbackValue((value) => value.calldate)
c.Sort()Follow these guidelines when writing a date format string:
- The units "y", "M", "d", "H", "h", "m", "s", and "t" each can be used to match with specific values
in a date string. See
FormatTimefor details about the units. - Only numeric day units are recognized; names like "Mon", "Tuesday", etc., are not recognized by
Container_DateParser. Container_DateParserwill match with both numeric month units and month names.- In addition to the units, RegEx is viable within the format string. The following restrictions
permit compatibility between the unit characters and RegEx:
- If the format string contains one or more literal "y", "M", "d", "H", "h", "m", "s" or "t"
characters, you must escape the date format units using this escape: \t{...}
- Example without using \t{...}:
DateStr := "2024-01-28 19:15" DateFormat := "yyyy-MM-dd HH:mm" Date := Container_Date(DateStr, DateFormat) MsgBox(Date.Year "-" Date.Month "-" Date.Day " " Date.Hour ":" Date.Minute) ; 2024-01-28 19:15
- Example with using \t{...}:
DateStr := "Voicemail From <1-555-555-5555> at 2024-01-28 07:15:20" DateFormat := "at \t{yyyy-MM-dd HH:mm:ss}" Date := Container_Date(DateStr, DateFormat) MsgBox(Date.Year "-" Date.Month "-" Date.Day " " Date.Hour ":" Date.Minute ":" Date.Second) ; 2024-01-28 07:15:20
- Example without using \t{...}:
- You can include multiple sets of \t escaped format units.
DateStr := "Voicemail From <1-555-555-5555> Received January 28, 2024 at 12:15:20 AM" DateFormat := "Received \t{MMMM dd, yyyy} at \t{hh:mm:ss tt}" ; Use case insensitive matching when matching a month by name. Date := Container_Date(DateStr, DateFormat, "i)") MsgBox(Date.Year "-" Date.Month "-" Date.Day " " Date.Hour ":" Date.Minute ":" Date.Second) ; 2024-01-28 00:15:20
- You can use the "?" quantifier.
DateStr1 := "Voicemail From <1-555-555-5555> Received January 28, 2024 at 12:15 AM" DateStr2 := "Voicemail From <1-555-555-5555> Received January 28, 2024 at 12:15:12 AM" DateFormat := "Received \t{MMMM dd, yyyy} at \t{hh:mm:?ss? tt}" ; Use case insensitive matching when matching a month by name. Date1 := Container_Date(DateStr1, DateFormat, "i)") Date2 := Container_Date(DateStr2, DateFormat, "i)") MsgBox(Date1.Year "-" Date1.Month "-" Date1.Day " " Date1.Hour ":" Date1.Minute ":" Date1.Second) ; 2024-01-28 00:15:00 Date2 := Container_Date(DateStr2, DateFormat) MsgBox(Date2.Year "-" Date2.Month "-" Date2.Day " " Date2.Hour ":" Date2.Minute ":" Date2.Second) ; 2024-01-28 00:15:12
- The match object is set to the property
Container_DateObj.Match. Include any extra subcapture groups that you are interested in.DateStr := "The child was born May 2, 1990, the year of the horse" DateFormat := "\t{MMMM d, yyyy}, the year of the (?<animal>\w+)" ; Use case insensitive matching when matching a month by name. Date := Container_Date(DateStr, DateFormat, "i)") MsgBox(Date.Year "-" Date.Month "-" Date.Day " " Date.Hour ":" Date.Minute ":" Date.Second) ; 1990-05-02 00:00:00 MsgBox(Date.Match["animal"]) ; horse
- If the format string contains one or more literal "y", "M", "d", "H", "h", "m", "s" or "t"
characters, you must escape the date format units using this escape: \t{...}
The CONTAINER_SORTTYPE_MISC sort type is for custom comparisons. When using CONTAINER_SORTTYPE_MISC,
your code only calls Container.Prototype.SetCallbackCompare; it does not call
Container.Prototype.SetCallbackValue. When values are compared, the values are passed to
ContainerObj.CallbackCompare as-is, and your function is expected to return a number indicating
the relationship between the two. See CONTAINER_SORTTYPE_MISC for an
example.
Iterative methods iterate over the values in the container sequentially. Like the other methods, there are sparse and non-sparse versions of each method.
Iterative methods have a parameter Callback. This is separate from the ContainerObj.CallbackValue
and ContainerObj.CallbackCompare functions; Callback never gets cached as a property by the
iterative methods.
Some of the iterative methods have a parameter ThisArg. See file
test\ThisArg-example.ahk
for details about using the ThisArg parameter.
This section lists the class static methods, instance methods, and instance properties. When
a property or method is listed as Container.Prototype.<Name>, that property exists on
Container.Prototype. When a property or method is listed as ContainerObj.<Name>, that property
is an own property that is added to the Container object some time during or after instantiation.
The following is a list of static methods.
Container.CbDateContainer.CbDateStrContainer.CbDateStrFromParserContainer.CbNumberContainer.CbStringContainer.CbStringPtrContainer.DateContainer.DateStrContainer.DateStrFromParserContainer.DateValueContainer.FromArrayContainer.MiscContainer.NumberContainer.StringContainer.StringPtrContainer.StrSplit
The following is a list of static properties.
Container.SortTypeSymbolList
In addition to the methods inherited from Array, Container has the following methods:
Container.Prototype.CompareContainer.Prototype.CondenseContainer.Prototype.CopyContainerObj.DateConvertContainerObj.DateConvertCbContainer.Prototype.DateInsertContainer.Prototype.DateInsertIfAbsentContainer.Prototype.DateInsertIfAbsentSparseContainer.Prototype.DateInsertListContainer.Prototype.DateInsertListSparseContainer.Prototype.DateInsertSparseContainer.Prototype.DatePreprocessContainer.Prototype.DateUpdateContainer.Prototype.DeepCloneContainer.Prototype.DeleteAllContainer.Prototype.DeleteAllSparseContainer.Prototype.DeleteValueContainer.Prototype.DeleteValueIfContainer.Prototype.DeleteValueIfSparseContainer.Prototype.DeleteValueSparseContainer.Prototype.EnumContainer.Prototype.EnumRangeContainer.Prototype.EnumRangeSparseContainer.Prototype.EveryContainer.Prototype.EverySparseContainer.Prototype.FindContainer.Prototype.FindAllContainer.Prototype.FindAllSparseContainer.Prototype.FindInequalityContainer.Prototype.FindInequalitySparseContainer.Prototype.FindSparseContainer.Prototype.FlatContainer.Prototype.ForEachContainer.Prototype.ForEachSparseContainer.Prototype.GetValueContainer.Prototype.GetValueSparseContainer.Prototype.HasValueContainer.Prototype.HasValueSparseContainer.Prototype.InsertContainer.Prototype.InsertIfAbsentContainer.Prototype.InsertIfAbsentSparseContainer.Prototype.InsertListContainer.Prototype.InsertListSparseContainer.Prototype.InsertSparseContainer.Prototype.InsertionSortContainer.Prototype.JoinContainer.Prototype.JoinExContainer.Prototype.MapContainer.Prototype.MapSparseContainer.Prototype.PurgeContainer.Prototype.PurgeSparseContainer.Prototype.PushExContainer.Prototype.QuickSortContainer.Prototype.ReduceContainer.Prototype.ReduceSparseContainer.Prototype.RemoveContainer.Prototype.RemoveAllContainer.Prototype.RemoveAllSparseContainer.Prototype.RemoveIfContainer.Prototype.RemoveIfSparseContainer.Prototype.RemoveSparseContainer.Prototype.ReverseContainer.Prototype.ReverseSparseContainer.Prototype.SearchContainer.Prototype.SearchAllContainer.Prototype.SearchAllSparseContainer.Prototype.SearchSparseContainer.Prototype.SetCallbackCompareContainer.Prototype.SetCallbackValueContainer.Prototype.SetCompareStringExContainer.Prototype.SetCompareDateContainer.Prototype.SetCompareDateStrContainer.Prototype.SetDateParserContainer.Prototype.SetSortTypeContainer.Prototype.SliceContainer.Prototype.SortContainer.Prototype.ToCbDateContainer.Prototype.ToCbDateStrContainer.Prototype.ToCbDateStrFromParserContainer.Prototype.ToCbNumberContainer.Prototype.ToCbStringContainer.Prototype.ToCbStringPtrContainer.Prototype.ToDateContainer.Prototype.ToDateStrContainer.Prototype.ToDateStrFromParserContainer.Prototype.ToDateValueContainer.Prototype.ToMiscContainer.Prototype.ToNumberContainer.Prototype.ToStringContainer.Prototype.ToStringPtrContainer.Prototype.ValidateSort
This section categorizes the instance methods into the following categories:
Methods that sort the values in the container.
Container.Prototype.InsertionSortContainer.Prototype.QuickSortContainer.Prototype.Sort
Methods that implement a binary search.
Methods that use a binary search to find a value / values in the container.
Container.Prototype.EnumRangeContainer.Prototype.EnumRangeSparseContainer.Prototype.FindContainer.Prototype.FindAllContainer.Prototype.FindAllSparseContainer.Prototype.FindInequalityContainer.Prototype.FindInequalitySparseContainer.Prototype.FindSparseContainer.Prototype.GetValueContainer.Prototype.GetValueSparse
Methods that use a binary search to insert a value into the container, retaining the sort order.
Container.Prototype.DateInsertContainer.Prototype.DateInsertIfAbsentContainer.Prototype.DateInsertIfAbsentSparseContainer.Prototype.DateInsertListContainer.Prototype.DateInsertListSparseContainer.Prototype.DateInsertSparseContainer.Prototype.InsertContainer.Prototype.InsertIfAbsentContainer.Prototype.InsertIfAbsentSparseContainer.Prototype.InsertListContainer.Prototype.InsertListSparseContainer.Prototype.InsertSparse
Methods that use a binary search to find, then delete a value / values, leaving the index / indices unset.
Container.Prototype.DeleteAllContainer.Prototype.DeleteAllSparseContainer.Prototype.DeleteValueContainer.Prototype.DeleteValueIfContainer.Prototype.DeleteValueIfSparseContainer.Prototype.DeleteValueSparse
Methods that use a binary search to find, then remove a value / values, shifting the values to the left to fill in the empty index / indices.
Container.Prototype.RemoveContainer.Prototype.RemoveAllContainer.Prototype.RemoveAllSparseContainer.Prototype.RemoveIfContainer.Prototype.RemoveIfSparseContainer.Prototype.RemoveSparse
Helper methods involved with using binary search and sort operations on date values.
ContainerObj.DateConvertContainerObj.DateConvertCbContainer.Prototype.DatePreprocessContainer.Prototype.DateUpdate
Methods that define the properties needed to use sort and binary search methods.
Container.Prototype.SetCallbackCompareContainer.Prototype.SetCallbackValueContainer.Prototype.SetCompareStringExContainer.Prototype.SetCompareDateContainer.Prototype.SetCompareDateStrContainer.Prototype.SetDateParserContainer.Prototype.SetSortTypeContainer.Prototype.ToCbDateContainer.Prototype.ToCbDateStrContainer.Prototype.ToCbDateStrFromParserContainer.Prototype.ToCbNumberContainer.Prototype.ToCbStringContainer.Prototype.ToCbStringPtrContainer.Prototype.ToDateContainer.Prototype.ToDateStrContainer.Prototype.ToDateStrFromParserContainer.Prototype.ToDateValueContainer.Prototype.ToMiscContainer.Prototype.ToNumberContainer.Prototype.ToStringContainer.Prototype.ToStringPtr
Methods that iterate the values in the container, performing some action on them.
Container.Prototype.CondenseContainer.Prototype.EveryContainer.Prototype.EverySparseContainer.Prototype.FlatContainer.Prototype.ForEachContainer.Prototype.ForEachSparseContainer.Prototype.HasValueContainer.Prototype.HasValueSparseContainer.Prototype.JoinContainer.Prototype.JoinExContainer.Prototype.MapContainer.Prototype.MapSparseContainer.Prototype.PurgeContainer.Prototype.PurgeSparseContainer.Prototype.ReduceContainer.Prototype.ReduceSparseContainer.Prototype.ReverseContainer.Prototype.ReverseSparseContainer.Prototype.SearchContainer.Prototype.SearchAllContainer.Prototype.SearchAllSparseContainer.Prototype.SearchSparse
Container.Prototype.CompareContainer.Prototype.CopyContainer.Prototype.DeepCloneContainer.Prototype.EnumContainer.Prototype.PushExContainer.Prototype.SliceContainer.Prototype.ValidateSort
In addition to the properties inherited from Array, Container has the following properties:
ContainerObj.CallbackCompareContainerObj.CallbackCompareValueContainerObj.CallbackDateInsertContainerObj.CallbackValueContainerObj.CompareDateCenturyContainerObj.CompareStringLocaleNameContainerObj.CompareStringNlsVersionInfoContainer.Prototype.DateParserContainerObj.SortType
This section categorizes the instance properties into the following categories:
Container.Prototype.DateParser
ContainerObj.CallbackCompareContainerObj.CallbackCompareValueContainerObj.CallbackDateInsertContainerObj.CallbackValueContainerObj.CompareDateCenturyContainerObj.CompareStringLocaleNameContainerObj.CompareStringNlsVersionInfoContainerObj.SortType
This section includes miscellaneous info not specifically related to Container but that might be
useful.
The following is a simple binary search written in AHK code. Container has many variations of
this same logic to meet any use case.
BinarySearch(arr, value, comparator) {
left := 1
right := arr.Length
while right - left > 4 {
i := right - Ceil((right - left) * 0.5)
if x := comparator(value, arr[i]) {
if x > 0 {
left := i
} else {
right := i
}
} else {
return i
}
}
i := left
loop right - i + 1 {
if comparator(value, arr[i]) {
++i
} else {
return i
}
}
}Parameter hints are a feature available in some code editors that, when activated, displays information about a function in a tooltip within the editor.
I've written 95% of my code in Visual Studio Code, so I do not know about other editors. If your code editor does not have this functionality, but you are interested in trying out a new editor, I recommend Visual Studio Code. Be sure to install thqby's AutoHotkey v2 Language Support extension. Also you will want to install a debugger. I have been using vscode-autohotkey-debug but I searched on https://marketplace.visualstudio.com/ right now and AutoHotKey Debug looks promising.
Visual Studio Code is massively customizable and can be intimidating to use at first, but you will get the hang of it over time. Parameter hints are invoked by the action called "Trigger parameter hints". You can customize the keyboard shortcut for this by opening Keyboard Shortcuts (Ctrl+Shift+P > Preferences: Open Keyboard Shortcuts) and searching "Trigger parameter hints".
-
2025-11-06 - v1.0.6
- BREAKING CHANGES
- Renamed
Container.Prototype.__EnumtoContainer.Prototype.Enum. The reason for the change is because overridingArray.Prototype.__Enumcaused the debugger vscode-autohotkey-debug to fail to display the container's items as child tree-view nodes beneath the container's tree-view node. I don't have the time to commit to figuring out how to adjust the debugger's code to work around this, so instead I renamed the method. Any code that callsContainer.Prototype.__Enumand that also expects to receive one of the custom enumerators will need to instead callContainer.Prototype.Enum.
- Renamed
- Changed:
Container_DateParserobjects now have a property "DateFormat" which contains the string date format that was passed to the first parameter ofContainer_DateParser.Prototype.__New.if A_LineFile == A_ScriptFullPathtoif !A_IsCompiled && A_LineFile == A_ScriptFullPathin test files.
- Fixed:
#include *i NlsVersionInfo.ahkto use the correct file name.
- BREAKING CHANGES
-
2025-10-10 - v1.0.5
- Added methods:
Container.Prototype.EnumRangeContainer.Prototype.EnumRangeSparseContainer.Prototype.__Enum
- Added file:
- test\test-EnumRange.ahk
- Changed:
- Removed value "CONTAINER_SORTTYPE_END" from
Container.SortTypeSymbolList. - Added
test_miscExamples.Enum,test_miscExamples.EnumRange, andtest_miscExamples.EnumRangeSparse.
- Removed value "CONTAINER_SORTTYPE_END" from
- Added methods:
-
2025-10-07 - v1.0.4
- Changed:
Container_SetConstantsnow sets a global variablecontainer_flag_constants_setto indicate thatContainer_SetConstantshas already been called.Container_SetConstantsnow has a parameterforcewhich allows the caller to force execution even ifContainer_SetConstantshas previously been called.
- Changed:
-
2025-10-06 - v1.0.3
- BREAKING CHANGES
- Removed parameter
Encodingfrom all methods that previously had it. Any code that called a method with a parameterEncodingthat used that parameter will need to delete that parameter. Any code that called a method with a parameterEncodingthat used a parameter that occurred afterEncodingwill need to shift those parameters to the left by 1. The following are affected methods:Container.StringPtrContainer.StringContainer.CbStringPtrContainer.CbStringContainer.Prototype.SetCompareStringExContainer.Prototype.ToStringPtrContainer.Prototype.ToStringContainer.Prototype.ToCbStringPtrContainer.Prototype.ToCbString
- Removed parameter
- Changed:
Container.StrSplitnow callsContainer.Prototype.ToStringfrom the newContainerinstance. The parameters forContainer.Prototype.ToStringwere added toContainer.StrSplit.
- Documentation:
- Added parameter hints to the remaining methods that did not yet have parameter hints.
- Fixed the parameter hint above
Container.Prototype.SetCallbackValue. Previously, it displayed the parameter hint details forContainer.Prototype.SetCallbackCompare. It now displays the correct information.
- BREAKING CHANGES
-
2025-10-04 - v1.0.2
- Added methods:
Container.Prototype.GetValueContainer.Prototype.GetValueSparse
- Changed:
- test\run-tests.ahk - Changed the name
testtoContainer_RunTests, added in tooltips, and added in aif A_LineFile == A_ScriptFullPathcondition to run the tests. - Simplified the
whilecondition in all of the "Find" methods.
- test\run-tests.ahk - Changed the name
- Added methods:
-
2025-10-04 - v1.0.1
- Added methods:
Container.Prototype.DateInsertListContainer.Prototype.DateInsertListSparseContainer.Prototype.InsertListContainer.Prototype.InsertListSparseContainer.Prototype.ValidateSort
- Added property:
Container.SortTypeSymbolList
- Added global functions:
Container_IndexToSymbolContainer_SetSortTypeContainer
- Added file:
- test\performance-test-InsertAll.ahk
- Added methods:
-
2025-10-03 - v1.0.0
- Released v1.0.0