This throws all sorts of flags...
TXShooter wrote:
I dunno if any of that is even possible in this language
It is, but you seem to be asking for a custom solution, seeing what you posted. Not a simple iniread and write. Your requirements also seem indecisive. You may also have to change how you are doing it. Why don't you just treat each event as an object, they are like the prime candidate for it. You don't need an array for each event field. You need an event as an object that contains fields.
Due to changes in the latest autohotkey v1 version (for ojbcount), I would simply go with an associative array and a rename of [Event1], [Event2], etc, in the ini to simply be an integer, i.e [1] and [2] and add a field inside the event called name, that will contain "Event1" or whatever name you want to give it. I would also suggest including as field the number as "ID", e.g ID = 1 or ID = 2.
So events[1] pulls event object attached to section name [1] in the ini (and not the first section found), and events[1].name pulls the name of event 1 i.e "Event1", events[1].date the date, etc.
If you ever need the count of this events object, then objcount() solves it, allowing you to create sparse objects, with keys 1, 3, 100 for example. Events[3] can return event 3, events being a conceptual associative array instead of linear, i.e 3 is not the "third" key by order, but a key called 3, similar to obj["boo"], boo being the key name.
TXShooter wrote:
so it doesn't get processed in the next file read.
You either read the whole ini file, parse it, store it into an object, manipulate what you need, then write back the whole thing overwriting it, or, parse the object and use iniwrite for each key value, but I think this is slower since the file has to be opened and closed each time for each ini function call.
Ultimately, reading the whole file and parsing it is less headachey.
Tip, autohotkey associative array keys are always stored in alphabetical order, instead of natural or the original order they were added. If you try to use a hypothetical event object with field names just as in the ini, the write back to file wont be in the order you have right now, unless you use iniwrite for each individual key value and not in bulk using pairs. Or if you try to use the sectionnames as keys in an associative array, the order of the sections may also get changed, as in "Event1", "Event100", "Event2", "Event3".
Are people editing the ini file manually or is it being exported from somewhere else?
Does the order of the data in the ini file matter? Does event1 need to be right in the first line? Another important question, does the order of each field in the ini file matter? (none of these should be a requirement)
Are events completed in order? (shouldn't be a requirement either)
If the order of the ini file matters, you want to use the data store as the viewer. That's even worse implementation from the getgo.
In your current way with fixed positions, if you decide to add another field to the event in the future, say something above "speaker", then Speaker is no longer [2], but [3] when bound to a fixed position. You now have to change every reference of [2] to [3] in the code, instead of simply pulling the fields or sections by name.
What does the section name stand for? the "event1" , "event2"? Is that the name of the event? Or is it the unique identifier? If it's the latter, why are you prefixing everything with "event", i think it should be pretty obvious what the ini file is for. Why not keep it as simple integers? You are reading an ini file containing event objects and their fields.
You say "X = Array.SectionNames[3] ; Would be "Event3"", ? so which is it, what's Event3 here? the event name?
"Event3" as in the object reference to "Event3" or "Event3" the string?
Isn't it more reasonable to actually do something like Array[3].name returns "Event3" string? with Array[3].date returning the date and so on? and Array[3] returning an object reference for event 3.
I assume "Event3" is like an internal name? and the title field is the name in the promotional material?
I edited some functions I had for other stuff to match this, using your current section names, to see if they help you figure out what you need to do or continue doing. You can put stuff inside a class, or whatever you come up with. No warranties or liabilities, so make a backup of the original ini file if you decide to try anything. I would also use JSON since it is way more compatible with other stuff and flexible.
Only problem being... it's written in Autohotkey v2, so... ¯\_(ツ)_/¯
Code: Select all
events := LoadEvents("events.ini")
Msgbox(events.3.id) ;// Shows 3
Msgbox(events.3.name) ;// Shows Event3
Msgbox(events.3.date) ;// Shows 2018.06.07
hello := events.3 ;// Copies event 3 object reference to hello
;// So you can do
Msgbox(hello.name) ;// Shows Event3
events.3.completed := "yes" ;// Sets completion
if eventsInProg := GetEventsByProgress(events, "no")
{
for k,v in eventsInProg
msgbox(k) ;// 1, 2 and 100.
eventsInProg.2.title:="HEEEEEEEEEEEEY"
}
UpdateEvents("events.ini", events) ;// Keeps order in existing file
DumpEvents("events-unordered.ini", events) ;// Won't keep order in any way.
;////////////////////////////////////////////////////////////////////
/* LoadEvents
Path: path to ini file
-
Return: associative array of ini elements
*/
LoadEvents(path)
{
;// Inline comments unsupported
if !path || !FileExist(path)
throw "Ini file does not exist."
sections := {}
sectionName := "•¶" ;// Non standard characters to ensure key value pairs are always inside a section
;// since an empty section name is possible for winapi i.e []
line := firstChar := key := value := ""
i := j := 0
Loop read, path
{
line := Trim(a_loopreadline) ;// QOL
firstChar := SubStr(line, 1, 1) ;// QOL
if !line || (firstChar = ";") ;// Empty/Comment line
continue
if (firstChar = "[") ;// Section definition
{
if !(j :=InStr(line, "]",, 2))
throw "Malformed section definition in ini file."
sectionName := SubStr(line, 2, j-2) ;// Can be empty
ID:=SubStr(sectionName, 6) ;// Get N from EventN
;// Store EventN as event.name and the ID/number as event.ID simply because objects
;// should contain their own information by themselves. That way even if the ID key is lost, the data always
;// resides inside the object. e.g eventvar:=events[id], now you don't know what number it is.
;// That means the current implementation of using "Event5" is bad :)
;// Or using an ini file for this is bad :D
;// Ini = initialization, not database.
sections[ID] := Object("Name", sectionName, "ID", ID)
continue
}
if (i := Instr(line, "="))
{
if (sectionName = "•¶")
throw "Every key value pair needs to be within a section definition, even if []"
key := SubStr(line, 1, i-1) ;// Left of =
value := SubStr(line, i+1) ;// Right of =
sections[ID][key] := value ;// Store it
}
else
continue ;// Line not recognized. Neither section definition nor key-value pair.
}
return sections
}
/* GetEventsByProgress
Events: associative array containing events
Progress: "yes" or "no"
-
Return: associative array containing events with the matching progress.
*/
GetEventsByProgress(events, progress)
{
matched := {}
count := 0
for id, event in events
{
if (event.completed = progress)
{
matched[id] := event ;// Will be storing object reference.
;// Any changes here will also be in the original array where the ini was loaded to.
++count
}
}
return (count ? matched : "")
}
/* ExportEvents
File: filepath of ini file
Events: associative array containing events
-
Return: empty
*/
UpdateEvents(file, events)
{
;// pairs:=""
for id, event in events
{
section:=event.name
for key, value in event
{
if (key = "name") || (key = "id") ;// Accounting for bad implementation
continue
IniWrite(value, file, section, key) ;// Preserves order if they already exist in the ini file.
;// Using the iniwrite gem where file is the second parameter.
;// pairs .= key "=" value "`n"
}
;// IniWrite(pairs, "initest.ini", section) ;// Won't preserve order.
;// pairs:=""
}
}
/* DumpEvents
Same same, but different, but still same.
*/
DumpEvents(file, events)
{
data:=""
for id, event in events
{
data .= "[" event.name "]`n"
for key, value in event
{
if (key = "name") || (key = "id")
continue
data .= key "=" value "`n"
}
}
if FileExist(file)
FileRecycle(file) ;// So you can get it back if you fuck up.
FileAppend(data, file) ;// Another gem where the file is the second parameter
}
;// RIP custom ObjCount() 2014-2018
ObjCount(obj)
{
return NumGet(&obj + 4*A_PtrSize)
}