AutoHotkey Community

It is currently May 27th, 2012, 8:18 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 21 posts ]  Go to page Previous  1, 2
Author Message
 Post subject:
PostPosted: December 25th, 2010, 2:55 am 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Latest work. New methods:
ToListView(), Search(), DeleteRow(), new MatchTypes in search methods (RegEx, contains, contains+, exact, exact+), LastFound property, etc. ...
.... I should start writing documentation :roll:
Code:
;===oTable testing area=================================================================
Variable =      ; column names are specified as first row
(
First name%A_Tab%Last name%A_Tab%Occupation%A_Tab%Notes
Jack%A_Tab%Gates%A_Tab%Driver%A_Tab%
Mark%A_Tab%Weber%A_Tab%Student%A_Tab%His father is a driver.
Jim%A_Tab%Tucker%A_Tab%Driver%A_Tab%
Jill%A_Tab%Lochte%A_Tab%Artist%A_Tab%
Jim%A_Tab%Tucker%A_Tab%Driver%A_Tab%
Jessica%A_Tab%Hickman%A_Tab%Student%A_Tab%
Mary%A_Tab%Jones%A_Tab%Teacher%A_Tab%Her favorite song is "D r i v e r".
Lenny%A_Tab%Stark%A_Tab%Driver%A_Tab%
Jack%A_Tab%Black%A_Tab%Actor%A_Tab%His wife is artist.
Tony%A_Tab%Jackman%A_Tab%Surfer%A_Tab%
Jonny%A_Tab%Poor%A_Tab%Beggar%A_Tab%
Scott%A_Tab%Jenstan%A_Tab%Teacher%A_Tab%Lives in New York.
)

; create simple gui for testing
Gui 1: Add, ListView, x5 y5 w500 h300 vGuiFoundList, First name|Last name|Occupation|Notes
Gui 1: Show, w510 h310 hide

oTable := Table_ObjCreate(Variable)   ; create table object
Variable =    ; not longer needed


;MsgBox % oTable.3.2   ; get value from [3. row, 2. column]
;oTable.1.1 := "Bobby"   ; set [1. row, 1. column] to "Bobby"

;MsgBox % oTable.ToString()   ; convert whole table object to string
;MsgBox % oTable.3.ToString()   ; convert 3. row object to string
;MsgBox % oTable.11.ToString("#")   ; convert 11. row object to string but use custom delimiter
;MsgBox % oTable.MaxIndex()   ; get number of rows (in future maybe: oTable.Count)


;=== Adding row ===
oTable.AddRow("Joe", "Newman", "Kiteboarder", "Freestyle & Wave", "Do not store not existing column!")   ; add row


;=== Deleting row ===
;oTable.DeleteRow("Jim", "Tucker", "Driver")   ; delete row (identified by its fields)
;oTable.Remove(3)   ; delete row (identified by its number)


;=== Simple search ===
; Search "Last name" column for containing "man" string
;MsgBox % oTable.SearchColumn("Last name", "man").ToString()    ;  convert search results to string


;=== Complexs search ===
; step 1: search "Occupation" and "Notes" columns for containing "Driver" or "artist" strings. "|" is query delimiter.
; step 2: search that search result again: search "First name" column for containing "J" string
;oFound := oTable.SearchColumn("Occupation|Notes", "Driver|artist")      ; store search results as object
;oFound2 := oFound.SearchColumn("First name", "J")      ; search oFound (second search filter)

; or shorter:   oFound2 := oTable.SearchColumn("Occupation|Notes", "Driver|artist").SearchColumn("First name", "J")   ; etc. --> multiple filters

;MsgBox % oFound2.ToString()      ; convert search results to string
;MsgBox % oFound2.2.ToString()   ; convert 2. row from search results to string
;MsgBox % oFound2.1.3   ; get [1. row, 3. column] field from search results
;MsgBox % oFound2.MaxIndex()   ; get number of found rows - from oFound2 (in future maybe: oFound2.Count)



;=== RegEx search ===   and LastFound property
; Search for all last names starting with "J" and ending with "an"
oFound3 := oTable.SearchColumn("Last name", "^J.*an$", "RegEx")   ; store search results as object
MsgBox % oFound3.ToString()   ; convert search results to string
MsgBox % oFound3.LastFound   ; each time after calling search method, row numbers of found rows are stored in LastFound key/property - read only.



;=== To ListView demonstration ===
F1::   ; shows whole oTable in ListView
LV_Delete()   ; empty ListView
oTable.ToListView()
Gui 1: Show
Return


F2::   ; finds all drivers and shows search results in ListView
LV_Delete()   ; empty ListView
oTable.SearchColumn("Occupation", "driver").ToListView()
Gui 1: Show
Return


F3::   ; Do a RegEx search and show search results in ListView
LV_Delete()   ; empty ListView
oFound := oTable.SearchColumn("Last name", "^J.*an$", "RegEx") ; Search for all last names starting with "J" and ending with "an".
oFound.ToListView()
Gui 1: Show
;MsgBox, 64, Numbers of found rows in latest search, % oFound.LastFound
;MsgBox, 64, oFound to string, % oFound.ToString()
Return

F4::   ; search whole oTable for containing string "new" and show search results in ListView
LV_Delete()   ; empty ListView
oTable.Search("New").ToListView()
;oTable.1.ToListView()   ; adds 1. row from oTable to ListView
Gui 1: Show
return








;===oTable Functions====================================================================
Table_Version() {
   return 0.09
}

Table_ObjCreate(InputVariable, ColumnsDelimiter="`t", RowsDelimiter= "`n") {   ; not finished
   
   static TableBase := Table_Base("SearchColumn Search ToString ToListView NewFromScheme AddRow DeleteRow DeleteRowAll Col2Num ", "Table_mTable_")
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")
   
   oTable := Object("base", Tablebase, "ColumnsDelimiter", ColumnsDelimiter, "RowsDelimiter", RowsDelimiter)
   oColumnNames := Object()
   
   Loop, parse, InputVariable, %RowsDelimiter%
   {
      CurRow := A_LoopField
      if A_index = 1   ; column names are specified as first row
      {
         Loop, parse, CurRow, %ColumnsDelimiter%
         {
            oColumnNames.Insert(A_LoopField)
            ColumnsCount++
         }
         oTable.ColumnNames := oColumnNames
         continue
      }
      RowNum := A_index-1
      %RowNum% := Object("base", RowBase, "table", oTable)
      
      StringSplit, field, CurRow, %ColumnsDelimiter%
      Loop, %ColumnsCount%
      %RowNum%.Insert(field%A_Index%)
      Loop, %ColumnsCount%
      field%A_Index% =
      
      oTable.Insert(%RowNum%)
   }
   return oTable
}

Table_Base(list, prefix) {
   base := Object()
   Loop Parse, list, %A_Space%
   base[A_LoopField] := prefix A_LoopField
   Return base
}

;====== oTable methods ======
Table_mTable_NewFromScheme(oTable) {   ; creates new empty table from table object template
   oNewTable := Object("base", oTable.base, "ColumnsDelimiter", oTable.ColumnsDelimiter
      , "RowsDelimiter", oTable.RowsDelimiter, "ColumnNames", oTable.ColumnNames)
   return oNewTable
}

Table_mTable_ToString(oTable, ColumnsDelimiter="", RowsDelimiter= "") {      ; converts table object to string
   if ColumnsDelimiter =
   ColumnsDelimiter := oTable.ColumnsDelimiter
   if RowsDelimiter =
   RowsDelimiter := oTable.RowsDelimiter

   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      RowString .= v2 ColumnsDelimiter
      RowString := RTrim(RowString, ColumnsDelimiter)
      TableString .= RowString RowsDelimiter
      RowString =
   }
   return RTrim(TableString, RowsDelimiter)
}

Table_mTable_ToListView(oTable) {   ; adds table object to ListView

   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      {
         f%A_index% := v2
         TotalFields ++
      }   
      LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
      Loop, %TotalFields%
      f%A_index% =
      TotalFields =
   }
}

Table_mTable_SearchColumn(oTable, ColumnsToSearch, StringsToSearch, MatchType="contains") {   ; performs search through columns
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")
   
   oFound := oTable.NewFromScheme()   ; create empty table from oTable template
   oFound.LastFound := ""
   ColumnsToSearch := oTable.Col2Num(ColumnsToSearch)
   
   if MatchType in contains,contains+,exact,exact+
   {
      StringReplace, StringsToSearch, StringsToSearch, `,, `,`,, all
      StringReplace, StringsToSearch, StringsToSearch, |, `,, all
   }
   
   if (MatchType = "exact+" or MatchType = "contains+")
   Table_RemoveWhitespaces(StringsToSearch)   ;  space, tab,  newline and carriage return  are not relevant for match (like "\s" in RegEx)

   ; About the coding style below: clumsy, longer way - but better performance for large tables - less "if evaluations" in loop.
   if MatchType = contains
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if CurField contains %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if (MatchType = "contains+")   ; space, tab,  newline and carriage return  are not relevant for match
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField], Table_RemoveWhitespaces(CurField)
            if CurField contains %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if MatchType = exact
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if CurField in %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if (MatchType = "exact+")   ; space, tab,  newline and carriage return  are not relevant for match
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField], Table_RemoveWhitespaces(CurField)
            if CurField in %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if MatchType = RegEx
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if RegExMatch(CurField, StringsToSearch)   ; StringsToSearch = NeedleRegEx
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   oFound.LastFound := LTrim(oFound.LastFound, ",")   ; stores row numbers of found rows in latest search
   return oFound
}

Table_mTable_Search(oTable, StringsToSearch, MatchType="contains") {   ; performs search through whole table
   ; quick improvisation or good permanent solution? Looks like #2. Will see...
   For k,v in oTable.ColumnNames
   ColumnsToSearch .= v "|"
   ColumnsToSearch := RTrim(ColumnsToSearch,"|")
   return oTable.SearchColumn(ColumnsToSearch, StringsToSearch, MatchType)
}

Table_mTable_AddRow(oTable,Fields*) {   ; adds new row to table
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")

   NewRowNum := oTable.MaxIndex() + 1
   %NewRowNum% := Object("base", RowBase, "table", oTable)
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      %NewRowNum%.Insert(v)
   }
   oTable.Insert(%NewRowNum%)
   return NewRowNum
}

Table_mTable_Col2Num(oTable, ColumnsToSearch) {      ; converts column name(s) to column number(s)
   StringReplace, ColumnsToSearch, ColumnsToSearch, `,, `,`,, all
   StringReplace, ColumnsToSearch, ColumnsToSearch, |, `,, all
   For k,v in oTable.ColumnNames
   {
      if v in %ColumnsToSearch%
      Found .= k "|"
   }
   return RTrim(Found, "|")
}


Table_mTable_DeleteRow(oTable, Fields*) {   ; deletes first matching row from table. (Identification by contents of fields)
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      RowStringToSearch .= v
   }
   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      RowString .= v2
      
      if (RowString = RowStringToSearch)
      {
         oTable.Remove(k)
         return 1
      }
      RowString =
   }
}

Table_mTable_DeleteRowAll(oTable, Fields*) {   ; deletes all matching rows from table. (Identification by contents of fields)
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      RowStringToSearch .= v
   }
   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      RowString .= v2
      if (RowString = RowStringToSearch)
      {
         KeysToDelete .= k "|"
         DeletedCount++
      }
      RowString =
   }
   KeysToDelete := RTrim(KeysToDelete, "|")
   Loop, Parse, KeysToDelete, |
   oTable.Remove(A_LoopField-A_index+1)
   return DeletedCount
}



;====== oRow methods ======
Table_mRow_ToString(oRow, ColumnsDelimiter="") {   ; converts row object to string
   if ColumnsDelimiter =
   ColumnsDelimiter := oRow.table.ColumnsDelimiter
   For k,v in oRow
   {
      if k is not integer   ; field keys are integers.
      continue
      RowString .= v ColumnsDelimiter
   }
   return RTrim(RowString, ColumnsDelimiter)
}

Table_mRow_ToListView(oRow) {   ; adds row object to ListView
   For k,v in oRow
   {
      if k is not integer   ; field keys are integers.
      continue
      f%k% := v
   }
   LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
}
   

;====== Shared, other ======
Table_RemoveWhitespaces(ByRef String) {   ; removes spaces, tabs,  newlines and carriage returns from string
   StringReplace, String, String, %A_Space%,, all
   StringReplace, String, String, %A_Tab%,, all
   StringReplace, String, String, `n,, all
   StringReplace, String, String, `r,, all
}

Edit: methods Search() and SearchColumn() will be merged in one, keeping all posibilities and easy syntax


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 26th, 2010, 11:54 am 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Latest work - version 0.11.
Code:
;===oTable testing area=================================================================
Variable =      ; column names are specified as first row
(
First name%A_Tab%Last name%A_Tab%Occupation%A_Tab%Notes
Jack%A_Tab%Gates%A_Tab%Driver%A_Tab%
Mark%A_Tab%Weber%A_Tab%Student%A_Tab%His father is a driver.
Jim%A_Tab%Tucker%A_Tab%Driver%A_Tab%
Jill%A_Tab%Lochte%A_Tab%Artist%A_Tab%Jack's sister.
Jessica%A_Tab%Hickman%A_Tab%Student%A_Tab%
Mary%A_Tab%Jones%A_Tab%Teacher%A_Tab%Her favorite song is "D r i v e r".
Lenny%A_Tab%Stark%A_Tab%Driver%A_Tab%
Jack%A_Tab%Black%A_Tab%Actor%A_Tab%His wife is artist.
Tony%A_Tab%Jackman%A_Tab%Surfer%A_Tab%
Jonny%A_Tab%Poor%A_Tab%Beggar%A_Tab%
Scott%A_Tab%Jenstan%A_Tab%Teacher%A_Tab%Lives in New York.
)


oTable := Table_ObjCreate(Variable)   ; create table object
Variable =    ; not longer needed

; create simple gui for testing
Gui 1: Add, ListView, x5 y5 w500 h300 grid, First name|Last name|Occupation|Notes
Gui 1: Show, w510 h310 hide



;=== Field management ===
;MsgBox % oTable.3.2   ; get value from [3. row, 2. column]
;oTable.1.1 := "Bobby"   ; set [1. row, 1. column] to "Bobby"


;=== Converting to numbers ===
;MsgBox % oTable.Row2Num("Jonny", "Poor", "Beggar")   ; get number of row whose fields are: Jonny, Poor, Beggar. (Identification by fields)
;MsgBox % oTable.Col2Num("Occupation")   ; get number of "Occupation" column
;MsgBox % oTable.Col2Num("First name|Notes")   ; get numbers of "First name" and "Notes" columns


;=== ToString method ===
;MsgBox % oTable.ToString()   ; convert whole table object to string
;MsgBox % oTable.3.ToString()   ; convert 3. row object to string
;MsgBox % oTable.11.ToString("#")   ; convert 11. row object to string but use custom delimiter


;=== MaxIndex (Count) ===
;MsgBox % oTable.MaxIndex()   ; get total number of rows (in future maybe: oTable.Count)
;MsgBox % oTable.ColumnNames.MaxIndex()   ; get total number of columns


;=== Row management ===
oTable.AddRow("Joe", "Newman", "Kiteboarder", "Freestyle & Wave")   ; add row (to the bottom)
;oTable.InsertRow(2 ,"Mike", "Insertovich", "Actor")   ; inserts new row number 2.
;oTable.ModifyRow(3 ,"Sergey", "Modifysky", "Actor")   ; modify row number 3.
;oTable.ModifyRow(0 ,"Chris", "Allman", "Actor")   ; modify all existing rows
;oTable.DeleteRow(2)   ; delete 2. row
;oTable.DeleteRow()      ; delete last row
;oTable.DeleteRow(0)   ; delete all rows


;=== Searching ===
;MsgBox % oTable.Search("Occupation", "Driver").ToString()    ; search Occupation column for containing string "driver"
;MsgBox % oTable.Search("Occupation|Notes", "Driver").ToString()    ; search Occupation and Notes columns for containing string "driver"
;MsgBox % oTable.Search("", "Driver", "containing+").ToString()    ; search whole table (all columns) for containing string "driver" but ignore withespaces
;MsgBox % oTable.Search("Last name", "^J.*an$", "RegEx").ToString() ; Search for all last names starting with "J" and ending with "an".
;MsgBox % oTable.Search("First name", "ny", "EndingWith").ToString()    ;  search first names ending with "ny"
;MsgBox % oTable.Search("Last name", "ja|bla", "StartingWith").ToString()    ;  search last names starting with "ja" or "bla"
;MsgBox % oTable.Search("", "Jack", "exactly").ToString()    ; search whole table (all columns) for string "Jack" (not containing, but exactly)


;=== Multiple filters search ===
; step 1: search "Occupation" and "Notes" columns for containing "Driver" or "artist" strings. "|" is query delimiter.
; step 2: search that search result again: search "First name" column for containing "J" string
;oFound := oTable.Search("Occupation|Notes", "Driver|artist")      ; store search results as object
;oFound2 := oFound.Search("First name", "J")      ; search oFound (second search filter)

; or shorter:   oFound2 := oTable.Search("Occupation|Notes", "Driver|artist").Search("First name", "J")   ; etc. --> multiple filters

;MsgBox % oFound2.ToString()      ; convert search results to string
;MsgBox % oFound2.2.ToString()   ; convert 2. row from search results to string
;MsgBox % oFound2.1.3   ; get [1. row, 3. column] field from search results
;MsgBox % oFound2.MaxIndex()   ; get number of found rows - from oFound2 (in future maybe: oFound2.Count)


;=== RegEx search ===   and LastFound property
; Search for all last names starting with "J" and ending with "an"
;oFound3 := oTable.Search("Last name", "^J.*an$", "RegEx")   ; store search results as object
;MsgBox % oFound3.ToString()   ; convert search results to string
;MsgBox % oFound3.LastFound   ; each time after calling search method, row numbers of found rows are stored in LastFound key/property - read only.


;=== ToListView method ===
F1::   ; shows whole oTable in ListView
LV_Delete()   ; empty ListView
oTable.ToListView()
Gui 1: Show
Return


F2::   ; finds all drivers and shows search results in ListView
LV_Delete()   ; empty ListView
oTable.Search("Occupation", "driver").ToListView()
Gui 1: Show
Return


F3::   ; Do a RegEx search and show search results in ListView
LV_Delete()   ; empty ListView
oFound := oTable.Search("Last name", "^J.*an$", "RegEx") ; Search for all last names starting with "J" and ending with "an".
oFound.ToListView()
Gui 1: Show
;MsgBox, 64, Numbers of found rows in latest search, % oFound.LastFound   ; LastFound property
;MsgBox, 64, oFound to string, % oFound.ToString()
Return


F4::   ; search whole table (all columns) for containing string "new" and show search results in ListView
LV_Delete()   ; empty ListView
oTable.Search("","New").ToListView()   ; empty 1. parameter means: search through whole table (all columns)
;oTable.1.ToListView()   ; adds 1. row from oTable to ListView
Gui 1: Show
return








;===oTable Functions====================================================================
Table_Version() {
   return 0.11
}

Table_ObjCreate(InputVariable, ColumnsDelimiter="`t", RowsDelimiter= "`n") {   ; not finished
   
   static TableBase := Table_Base("Search ToString ToListView NewFromScheme AddRow InsertRow ModifyRow DeleteRow Col2Num Row2Num", "Table_mTable_")
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")
   
   oTable := Object("base", Tablebase, "ColumnsDelimiter", ColumnsDelimiter, "RowsDelimiter", RowsDelimiter)
   oColumnNames := Object()
   
   Loop, parse, InputVariable, %RowsDelimiter%
   {
      CurRow := A_LoopField
      if A_index = 1   ; column names are specified as first row
      {
         Loop, parse, CurRow, %ColumnsDelimiter%
         {
            oColumnNames.Insert(A_LoopField)
            ColumnsCount++
         }
         oTable.ColumnNames := oColumnNames
         continue
      }
      RowNum := A_index-1
      %RowNum% := Object("base", RowBase, "table", oTable)
      
      StringSplit, field, CurRow, %ColumnsDelimiter%
      Loop, %ColumnsCount%
      %RowNum%.Insert(field%A_Index%)
      Loop, %ColumnsCount%
      field%A_Index% =
      
      oTable.Insert(%RowNum%)
   }
   return oTable
}

Table_Base(list, prefix) {
   base := Object()
   Loop Parse, list, %A_Space%
   base[A_LoopField] := prefix A_LoopField
   Return base
}

;====== oTable methods ======
Table_mTable_AddRow(oTable,Fields*) {   ; adds new row to table (to the bottom)
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")

   NewRowNum := oTable.MaxIndex() + 1
   %NewRowNum% := Object("base", RowBase, "table", oTable)
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      %NewRowNum%.Insert(v)
   }
   oTable.Insert(%NewRowNum%)
   return NewRowNum
}

Table_mTable_InsertRow(oTable, NewRowNum ,Fields*) {   ; inserts new row in table
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")
   
   if NewRowNum = 0   ; not allowed
   return
   
   if (NewRowNum > oTable.MaxIndex())
   NewRowNum := oTable.MaxIndex() + 1   ; add as last row
   
   %NewRowNum% := Object("base", RowBase, "table", oTable)
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      %NewRowNum%.Insert(v)
   }
   oTable.Insert(NewRowNum, %NewRowNum%)
}

Table_mTable_ModifyRow(oTable, RowToModifyNumber, Fields*) {   ; modifies row(s)
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")

   oModifyedRow := Object("base", RowBase, "table", oTable)
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      oModifyedRow.Insert(v)
   }
   if RowToModifyNumber = 0   ; modify all existing rows
   {
      Loop, % oTable.MaxIndex()
      oTable[A_Index] := oModifyedRow
   }
   else   ; modify specified row
   oTable[RowToModifyNumber] := oModifyedRow
}

Table_mTable_DeleteRow(oTable, RowToDeleteNumber="") {   ; deletes row(s)
   if (RowToDeleteNumber = 0)   ; delete all existing rows
   oTable.Remove(1, oTable.MaxIndex())
   Else if (RowToDeleteNumber = "")   ; delete last row
   oTable.Remove()
   else   ; delete specified row
   oTable.Remove(RowToDeleteNumber)
}



Table_mTable_Col2Num(oTable, ColumnsToSearch) {      ; converts column name(s) to column number(s)
   StringReplace, ColumnsToSearch, ColumnsToSearch, `,, `,`,, all
   StringReplace, ColumnsToSearch, ColumnsToSearch, |, `,, all
   For k,v in oTable.ColumnNames
   {
      if v in %ColumnsToSearch%
      Found .= k "|"
   }
   return RTrim(Found, "|")
}

Table_mTable_Row2Num(oTable, Fields*) {   ; converts row identified by its fields to number. First matching.
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      RowStringToSearch .= v
   }
   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      RowString .= v2
      
      if (RowString = RowStringToSearch)
      return k
      
      RowString =
   }
}

Table_mTable_NewFromScheme(oTable) {   ; creates new empty table from table object template
   oNewTable := Object("base", oTable.base, "ColumnsDelimiter", oTable.ColumnsDelimiter
      , "RowsDelimiter", oTable.RowsDelimiter, "ColumnNames", oTable.ColumnNames)
   return oNewTable
}



Table_mTable_Search(oTable, ColumnsToSearch, StringsToSearch, MatchType="containing") {   ; performs search through columns or whole table
   /* Parameters:
   ColumnsToSearch      "|" delimited list of columns to search. If empty (""), search through whole table (all columns.)
   StringsToSearch      "|" delimited list of strings to search except in RegEx MatchType.
   MatchType         Containing, Exactly, StartingWith, EndingWith, RegEx,
                  Containing+, Exactly+, StartingWith+, EndingWith+
                  "+" suffix means more permissive match type where spaces, tabs, newlines and carriage returns are not relevant for match.
   */
      
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")
   oFound := oTable.NewFromScheme()   ; create empty table from oTable template

   If (ColumnsToSearch = "")      ; search through all columns - whole table.
   {
      For k,v in oTable.ColumnNames
      ColumnsToSearch .= A_Index "|"
      ColumnsToSearch := RTrim(ColumnsToSearch,"|")
   }
   else   ; search through specified columns
   ColumnsToSearch := oTable.Col2Num(ColumnsToSearch)
   
   If (SubStr(MatchType,0) = "+")   ; more permissive match type where spaces, tabs, newlines and carriage returns are not relevant for match.
   {
      IgnoreWhitespaces = 1
      StringTrimRight, MatchType, MatchType, 1
      Table_RemoveWhitespaces(StringsToSearch)   ;  like RegExReplace(StringsToSearch, "\s")
   }
   
   if MatchType in Containing,Exactly   ; prepare matchlist
   {
      StringReplace, StringsToSearch, StringsToSearch, `,, `,`,, all
      StringReplace, StringsToSearch, StringsToSearch, |, `,, all
   }
   
   if MatchType = Containing
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if IgnoreWhitespaces
            Table_RemoveWhitespaces(CurField)
            if CurField contains %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if MatchType = Exactly
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if IgnoreWhitespaces
            Table_RemoveWhitespaces(CurField)
            if CurField in %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if MatchType = StartingWith
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if IgnoreWhitespaces
            Table_RemoveWhitespaces(CurField)
            Loop, parse, StringsToSearch, "|"
            {
               if (SubStr(CurField,1,StrLen(A_LoopField)) = A_LoopField)
               {
                  %k% := Object("base", RowBase, "table", oTable)
                  For   k2,v2 in oTable[k]
                  %k%.Insert(v2)
                  oFound.Insert(%k%)
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
   }
   else if MatchType = EndingWith
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if IgnoreWhitespaces
            Table_RemoveWhitespaces(CurField)
            Loop, parse, StringsToSearch, "|"
            {
               if StrLen(A_LoopField) = 1
               SubCurField := SubStr(CurField,0)
               else
               SubCurField := SubStr(CurField, - (StrLen(A_LoopField) - 1))
               if (SubCurField = A_LoopField)
               {
                  %k% := Object("base", RowBase, "table", oTable)
                  For   k2,v2 in oTable[k]
                  %k%.Insert(v2)
                  oFound.Insert(%k%)
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
   }
   else if MatchType = RegEx
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if RegExMatch(CurField, StringsToSearch)   ; StringsToSearch = NeedleRegEx
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   
   oFound.LastFound := LTrim(oFound.LastFound, ",")   ; stores row numbers of found rows in latest search
   return oFound
}

Table_mTable_ToString(oTable, ColumnsDelimiter="", RowsDelimiter= "") {      ; converts table object to string
   if ColumnsDelimiter =
   ColumnsDelimiter := oTable.ColumnsDelimiter
   if RowsDelimiter =
   RowsDelimiter := oTable.RowsDelimiter

   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      RowString .= v2 ColumnsDelimiter
      RowString := RTrim(RowString, ColumnsDelimiter)
      TableString .= RowString RowsDelimiter
      RowString =
   }
   return RTrim(TableString, RowsDelimiter)
}

Table_mTable_ToListView(oTable) {   ; puts table object to ListView

   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      {
         f%A_index% := v2
         TotalFields ++
      }   
      LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
      Loop, %TotalFields%
      f%A_index% =
      TotalFields =
   }
}

;====== oRow methods ======
Table_mRow_ToString(oRow, ColumnsDelimiter="") {   ; converts row object to string
   if ColumnsDelimiter =
   ColumnsDelimiter := oRow.table.ColumnsDelimiter
   For k,v in oRow
   {
      if k is not integer   ; field keys are integers.
      continue
      RowString .= v ColumnsDelimiter
   }
   return RTrim(RowString, ColumnsDelimiter)
}

Table_mRow_ToListView(oRow) {   ; puts row object to ListView
   For k,v in oRow
   {
      if k is not integer   ; field keys are integers.
      continue
      f%k% := v
   }
   LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
}
   

;====== Shared, other ======
Table_RemoveWhitespaces(ByRef String) {   ; removes spaces, tabs,  newlines and carriage returns from string. Like RegExReplace(String, "\s")
   StringReplace, String, String, %A_Space%,, all
   StringReplace, String, String, %A_Tab%,, all
   StringReplace, String, String, `n,, all
   StringReplace, String, String, `r,, all
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 28th, 2010, 3:35 pm 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Latest work - version 0.18. Although not completely finished, [object] Table is usable now.
Code:
;===oTable testing area=================================================================
Variable =      ; column names are specified as first row
(
First name%A_Tab%Last name%A_Tab%Occupation%A_Tab%Notes
Jack%A_Tab%Gates%A_Tab%Driver%A_Tab%
Mark%A_Tab%Weber%A_Tab%Student%A_Tab%His father is a driver.
Jim%A_Tab%Tucker%A_Tab%Driver%A_Tab%
Jill%A_Tab%Lochte%A_Tab%Artist%A_Tab%Jack's sister.
Jessica%A_Tab%Hickman%A_Tab%Student%A_Tab%
Mary%A_Tab%Jones%A_Tab%Teacher%A_Tab%Her favorite song is "D r i v e r".
Lenny%A_Tab%Stark%A_Tab%Driver%A_Tab%
Jack%A_Tab%Black%A_Tab%Actor%A_Tab%His wife is artist.
Tony%A_Tab%Jackman%A_Tab%Surfer%A_Tab%
Jonny%A_Tab%Poor%A_Tab%Beggar%A_Tab%
Scott%A_Tab%Jenstan%A_Tab%Teacher%A_Tab%Lives in New York.
)


oTable := Table_ObjCreate(Variable)   ; creates table object from variable
Variable =    ; not longer needed



; create simple gui for testing
Gui 1: Add, ListView, x5 y5 w500 h300 grid, % oTable.HeaderToString("|")   ; converts table's header (first row) to string.
Gui 1: Show, w510 h310 hide




;=== Field management ===
;MsgBox % oTable.3.2   ; get value from [3. row, 2. column]
;oTable.1.1 := "Bobby"   ; set [1. row, 1. column] to "Bobby"


;=== Converting to numbers ===
;MsgBox % oTable.Row2Num("Jonny", "Poor", "Beggar")   ; get number of row whose fields are: Jonny, Poor, Beggar. (Identification by fields)
;MsgBox % oTable.Col2Num("Occupation")   ; get number of "Occupation" column
;MsgBox % oTable.Col2Num("First name|Notes")   ; get numbers of "First name" and "Notes" columns


;=== ToString method ===
;MsgBox % oTable.ToString()   ; convert whole table object to string
;MsgBox % oTable.3.ToString()   ; convert 3. row object to string
;MsgBox % oTable.11.ToString("#")   ; convert 11. row object to string but use custom delimiter
;MsgBox % oTable.HeaderToString()   ; converts table's header (first row) to string


;=== MaxIndex (Count) ===
;MsgBox % oTable.MaxIndex()   ; get total number of rows (in future maybe: oTable.Count)
;MsgBox % oTable.ColumnNames.MaxIndex()   ; get total number of columns


;=== Row management ===
oTable.AddRow("Joe", "Newman", "Kiteboarder", "Freestyle & Wave")   ; add row (to the bottom)
;oTable.InsertRow(2 ,"Mike", "Insertovich", "Actor")   ; inserts new row number 2.
;oTable.ModifyRow(3 ,"Sergey", "Modifysky", "Actor")   ; modify row number 3.
;oTable.ModifyRow(0 ,"Chris", "Allman", "Actor")   ; modify all existing rows
;oTable.DeleteRow(2)   ; delete 2. row
;oTable.DeleteRow()      ; delete last row
;oTable.DeleteRow(0)   ; delete all rows


;=== Searching ===
;MsgBox % oTable.Search("Occupation", "Driver").ToString()    ; search Occupation column for containing string "driver"
;MsgBox % oTable.Search("Occupation|Notes", "Driver").ToString()    ; search Occupation and Notes columns for containing string "driver"
;MsgBox % oTable.Search("", "Driver", "containing+").ToString()    ; search whole table (all columns) for containing string "driver" but ignore withespaces
;MsgBox % oTable.Search("Last name", "^J.*an$", "RegEx").ToString() ; Search for all last names starting with "J" and ending with "an".
;MsgBox % oTable.Search("First name", "ny", "EndingWith").ToString()    ;  search first names ending with "ny"
;MsgBox % oTable.Search("Last name", "ja|bla", "StartingWith").ToString()    ;  search last names starting with "ja" or "bla"
;MsgBox % oTable.Search("", "Jack", "exactly").ToString()    ; search whole table (all columns) for string "Jack" (not containing, but exactly)


;=== Multiple filters search ===
; step 1: search "Occupation" and "Notes" columns for containing "Driver" or "artist" strings. "|" is query delimiter.
; step 2: search that search result again: search "First name" column for containing "J" string
;oFound := oTable.Search("Occupation|Notes", "Driver|artist")      ; store search results as object
;oFound2 := oFound.Search("First name", "J")      ; search oFound (second search filter)

; or shorter:   oFound2 := oTable.Search("Occupation|Notes", "Driver|artist").Search("First name", "J")   ; etc. --> multiple filters

;MsgBox % oFound2.ToString()      ; convert search results to string
;MsgBox % oFound2.2.ToString()   ; convert 2. row from search results to string
;MsgBox % oFound2.1.3   ; get [1. row, 3. column] field from search results
;MsgBox % oFound2.MaxIndex()   ; get number of found rows - from oFound2 (in future maybe: oFound2.Count)


;=== RegEx search ===   and LastFound property
; Search for all last names starting with "J" and ending with "an"
;oFound3 := oTable.Search("Last name", "^J.*an$", "RegEx")   ; store search results as object
;MsgBox % oFound3.ToString()   ; convert search results to string
;MsgBox % oFound3.LastFound   ; each time after calling search method, row numbers of found rows are stored in LastFound key/property - read only.


;=== Massive StringReplace ===
;oTable.StringReplace("y", "X", ".", " :)")   ; replaces "param1" with "param2", "param3" with "param4" (etc.) in all fields in table object.


;=== Interacting with files ===
;oTable := Table_ObjCreate(A_ScriptDir "\Table file.txt")   ; creates table object from constructor file
;oTable.Save()   converts table object to string and saves it to its constructor file. Use only if oTable is created from file.
;oTable.SaveAs(A_ScriptDir "\Table file.txt")      ; converts table object to string and saves it to specified file.
;oTable.Open()   ; opens table's constructor file in Notepad if it exists
;oTable.Reload()   ; reads table's constructor file again and reconstructs table object if constructor file exists

;oTable.SaveAs(A_ScriptDir "\Table file.txt","|","#")      ; converts table object to string and saves it to specified file but uses custom delimiters.
; When constructing table from such file, must specify custom delimiters -->  oTable := Table_ObjCreate(A_ScriptDir "\Table file.txt","|","#")




;=== ToListView method ===
F1::   ; shows whole oTable in ListView
LV_Delete()   ; empty ListView
oTable.ToListView()
Gui 1: Show
Return


F2::   ; finds all drivers and shows search results in ListView
LV_Delete()   ; empty ListView
oTable.Search("Occupation", "driver").ToListView()
Gui 1: Show
Return


F3::   ; Do a RegEx search and show search results in ListView
LV_Delete()   ; empty ListView
oFound := oTable.Search("Last name", "^J.*an$", "RegEx") ; Search for all last names starting with "J" and ending with "an".
oFound.ToListView()
Gui 1: Show
;MsgBox, 64, Numbers of found rows in latest search, % oFound.LastFound   ; LastFound property
;MsgBox, 64, oFound to string, % oFound.ToString()
Return


F4::   ; search whole table (all columns) for containing string "new" and show search results in ListView
LV_Delete()   ; empty ListView
oTable.Search("","New").ToListView()   ; empty 1. parameter means: search through whole table (all columns)
;oTable.1.ToListView()   ; adds 1. row from oTable to ListView
Gui 1: Show
return



/*   
;=== oTable - ListView interaction ===
d::oTable.LVDelete()   ; delete selected row
a::oTable.LVAdd("Mia","Addstan","Driver")   ; add new row

e::      ; edit selected row
CurFields =
oCurFields := oTable.LVModify1()   ; returns row's to modify fields. Must be called prior to oTable.LVModify2()
for k,v in oCurFields
CurFields .= v "|"
CurFields := RTrim(CurFields,"|")

if (CurFields = "")   ; nothing selected
return

InputBox, NewRow,, Current Fields:  %CurFields%,,,,,,,,%CurFields%
if ErrorLevel
return

oNewFields := SplitToObj(NewRow)
oTable.LVModify2(oNewFields)   ; modifies row in ListView and oTable
return
*/









;===oTable Functions====================================================================
Table_Version() {
   return 0.18
}

Table_ObjCreate(InputVariableOfFile, ColumnsDelimiter="`t", RowsDelimiter= "`n") {
   
   static TableBase := Table_Base("HeaderToString StringReplace Search ToString ToListView NewFromScheme AddRow InsertRow ModifyRow DeleteRow Col2Num Row2Num Open Save SaveAs Reload Row2NumL LVSelInfo LVDelete LVAdd LVModify1 LVModify2", "Table_mTable_")
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")
   
   oTable := Object("base", Tablebase, "ColumnsDelimiter", ColumnsDelimiter, "RowsDelimiter", RowsDelimiter)
   oColumnNames := Object()
   
   IfExist, %InputVariableOfFile%   ; construct table from file
   {
      oFile := FileOpen(InputVariableOfFile, "r `n", "UTF-8")   ; hard coded or option...?
      InputVariable := oFile.Read()
      oFile.Close()
      oTable.FilePath := InputVariableOfFile   ; store constructor file's FilePath
   }
   else   ; construct table from variable
   InputVariable := InputVariableOfFile
   
   Loop, parse, InputVariable, %RowsDelimiter%
   {
      CurRow := A_LoopField
      if A_index = 1   ; column names are specified as first row
      {
         Loop, parse, CurRow, %ColumnsDelimiter%
         {
            oColumnNames.Insert(A_LoopField)
            ColumnsCount++
         }
         oTable.ColumnNames := oColumnNames
         continue
      }
      RowNum := A_index-1
      %RowNum% := Object("base", RowBase, "table", oTable)
      
      StringSplit, field, CurRow, %ColumnsDelimiter%
      Loop, %ColumnsCount%
      %RowNum%.Insert(field%A_Index%)
      Loop, %ColumnsCount%
      field%A_Index% =
      
      oTable.Insert(%RowNum%)
   }
   return oTable
}

Table_Base(list, prefix) {
   base := Object()
   Loop Parse, list, %A_Space%
   base[A_LoopField] := prefix A_LoopField
   Return base
}

;====== oTable methods ======
Table_mTable_StringReplace(oTable,params*) { ; replaces "param1" with "param2", "param3" with "param4" (etc.) in all fields in table object
   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]   ; v2 = field
      {
         c := 0
         For k3,v3 in params
         {
            c++
            if c = 1
            {
               st := v3
               continue
            }
            rt := v3, c := 0
            StringReplace, v2, v2, %st%, %rt%, all
         }
         oTable[k][k2] := v2
      }
   }
}

Table_mTable_Open(oTable) {   ; opens table's constructor file in Notepad if it exists
   FilePath := oTable.FilePath
   IfExist, %FilePath%
   Run, notepad "%FilePath%"
}

Table_mTable_Save(oTable) {   ; converts table object to string and saves it to its constructor file. Use only if oTable is created from file.
   FilePath := oTable.FilePath      ; automatically stored when creating oTable from file
   if (FilePath = "")
   return
   ColumnsDelimiter := oTable.ColumnsDelimiter, RowsDelimiter := oTable.RowsDelimiter
   oTable.SaveAs(FilePath)
   return 1
}

Table_mTable_SaveAs(oTable, FilePath, ColumnsDelimiter="", RowsDelimiter= "") {   ; converts table object to string and saves it to specified file
   if ColumnsDelimiter =
   ColumnsDelimiter := oTable.ColumnsDelimiter
   if RowsDelimiter =
   RowsDelimiter := oTable.RowsDelimiter
   
   FileContents := oTable.HeaderToString(ColumnsDelimiter) RowsDelimiter oTable.ToString(ColumnsDelimiter, RowsDelimiter)
   
   oFile := FileOpen(FilePath, "w `n", "UTF-8")   ; creates a new file, overwriting any existing file.
   oFile.Write(FileContents)
   oFile.Close()
}

Table_mTable_Reload(ByRef oTable) {   ; reads table's constructor file again and reconstructs table object if constructor file exists
   static TableBase := Table_Base("HeaderToString StringReplace Search ToString ToListView NewFromScheme AddRow InsertRow ModifyRow DeleteRow Col2Num Row2Num Open Save SaveAs Reload Row2NumL LVSelInfo LVDelete LVAdd LVModify1 LVModify2", "Table_mTable_")
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")
   
   FilePath := oTable.FilePath, ColumnsDelimiter := oTable.ColumnsDelimiter, RowsDelimiter := oTable.RowsDelimiter   ; collect info from old table object
   IfNotExist, %FilePath%   ; if constructor file doesn't exist
   return   ; return and leave old table object as is
   
   oTable =   ; destroy old table object
   
   ; reconstruct table object
   oTable := Object("base", Tablebase, "ColumnsDelimiter", ColumnsDelimiter, "RowsDelimiter", RowsDelimiter)
   oColumnNames := Object()
   
   oTable.FilePath := FilePath
   oFile := FileOpen(FilePath, "r `n", "UTF-8")
   InputVariable := oFile.Read()
   oFile.Close()
   
   Loop, parse, InputVariable, %RowsDelimiter%
   {
      CurRow := A_LoopField
      if A_index = 1   ; column names are specified as first row
      {
         Loop, parse, CurRow, %ColumnsDelimiter%
         {
            oColumnNames.Insert(A_LoopField)
            ColumnsCount++
         }
         oTable.ColumnNames := oColumnNames
         continue
      }
      RowNum := A_index-1
      %RowNum% := Object("base", RowBase, "table", oTable)
      
      StringSplit, field, CurRow, %ColumnsDelimiter%
      Loop, %ColumnsCount%
      %RowNum%.Insert(field%A_Index%)
      Loop, %ColumnsCount%
      field%A_Index% =
      
      oTable.Insert(%RowNum%)
   }
   return oTable   ; not necessary as first param is ByRef, but keep it
}

Table_mTable_AddRow(oTable,Fields*) {   ; adds new row to table (to the bottom)
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")

   NewRowNum := oTable.MaxIndex() + 1
   %NewRowNum% := Object("base", RowBase, "table", oTable)
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      %NewRowNum%.Insert(v)
   }
   oTable.Insert(%NewRowNum%)
   return NewRowNum
}

Table_mTable_InsertRow(oTable, NewRowNum ,Fields*) {   ; inserts new row in table
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")
   
   if NewRowNum = 0   ; not allowed
   return
   
   if (NewRowNum > oTable.MaxIndex())
   NewRowNum := oTable.MaxIndex() + 1   ; add as last row
   
   %NewRowNum% := Object("base", RowBase, "table", oTable)
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      %NewRowNum%.Insert(v)
   }
   oTable.Insert(NewRowNum, %NewRowNum%)
}

Table_mTable_ModifyRow(oTable, RowToModifyNumber, Fields*) {   ; modifies row(s)
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")

   oModifyedRow := Object("base", RowBase, "table", oTable)
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      oModifyedRow.Insert(v)
   }
   if RowToModifyNumber = 0   ; modify all existing rows
   {
      Loop, % oTable.MaxIndex()
      oTable[A_Index] := oModifyedRow
   }
   else   ; modify specified row
   oTable[RowToModifyNumber] := oModifyedRow
}

Table_mTable_DeleteRow(oTable, RowToDeleteNumber="") {   ; deletes row(s)
   if (RowToDeleteNumber = 0)   ; delete all existing rows
   {
      oTable.Remove(1, oTable.MaxIndex())
      return
   }
   Else if (RowToDeleteNumber = "")   ; delete last row
   {
      oDeletedRow := Object()
      LastRN := oTable.MaxIndex()
      oDeletedRow := oTable[LastRN]
      oTable.Remove(LastRN)
      return oDeletedRow
   }
   else   ; delete specified row
   {
      oDeletedRow := Object()
      oDeletedRow := oTable[RowToDeleteNumber]
      oTable.Remove(RowToDeleteNumber)
      return oDeletedRow
   }
}



Table_mTable_Col2Num(oTable, ColumnsToSearch) {      ; converts column name(s) to column number(s)
   StringReplace, ColumnsToSearch, ColumnsToSearch, `,, `,`,, all
   StringReplace, ColumnsToSearch, ColumnsToSearch, |, `,, all
   For k,v in oTable.ColumnNames
   {
      if v in %ColumnsToSearch%
      Found .= k "|"
   }
   return RTrim(Found, "|")
}

Table_mTable_Row2Num(oTable, Fields*) {   ; converts row identified by its fields to number. First matching.
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      RowStringToSearch .= v
   }
   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      RowString .= v2
      
      if (RowString = RowStringToSearch)
      return k
      
      RowString =
   }
}

Table_mTable_NewFromScheme(oTable) {   ; creates new empty table from table object template
   oNewTable := Object("base", oTable.base, "ColumnsDelimiter", oTable.ColumnsDelimiter
      , "RowsDelimiter", oTable.RowsDelimiter, "ColumnNames", oTable.ColumnNames)
   return oNewTable
}



Table_mTable_Search(oTable, ColumnsToSearch, StringsToSearch, MatchType="containing") {   ; performs search through columns or whole table
   /* Parameters:
   ColumnsToSearch      "|" delimited list of columns to search. If empty (""), search through whole table (all columns.)
   StringsToSearch      "|" delimited list of strings to search except in RegEx MatchType.
   MatchType         Containing, Exactly, StartingWith, EndingWith, RegEx,
                  Containing+, Exactly+, StartingWith+, EndingWith+
                  "+" suffix means more permissive match type where spaces, tabs, newlines and carriage returns are not relevant for match.
   */
      
   static RowBase := Table_Base("ToString ToListView", "Table_mRow_")
   oFound := oTable.NewFromScheme()   ; create empty table from oTable template

   If (ColumnsToSearch = "")      ; search through all columns - whole table.
   {
      For k,v in oTable.ColumnNames
      ColumnsToSearch .= A_Index "|"
      ColumnsToSearch := RTrim(ColumnsToSearch,"|")
   }
   else   ; search through specified columns
   ColumnsToSearch := oTable.Col2Num(ColumnsToSearch)
   
   If (SubStr(MatchType,0) = "+")   ; more permissive match type where spaces, tabs, newlines and carriage returns are not relevant for match.
   Table_StringReplace(StringsToSearch, A_Space, "", A_Tab, "", "`n", "", "`r", "")   ; like RegExReplace(StringsToSearch, "\s")

   
   if MatchType in Containing,Exactly,Containing+,Exactly+   ; prepare matchlist
   {
      StringReplace, StringsToSearch, StringsToSearch, `,, `,`,, all
      StringReplace, StringsToSearch, StringsToSearch, |, `,, all
   }
   
   ; About coding style below: longer way, but better performance due to less "if evaluations" in loop for MatchTypes not containing "+".
   if MatchType = Containing
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if CurField contains %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if MatchType = Exactly
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if CurField in %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if MatchType = StartingWith
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            Loop, parse, StringsToSearch, "|"
            {
               if (SubStr(CurField,1,StrLen(A_LoopField)) = A_LoopField)
               {
                  %k% := Object("base", RowBase, "table", oTable)
                  For   k2,v2 in oTable[k]
                  %k%.Insert(v2)
                  oFound.Insert(%k%)
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
   }
   else if MatchType = EndingWith
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            Loop, parse, StringsToSearch, "|"
            {
               if (SubStr(CurField, - (StrLen(A_LoopField) - 1)) = A_LoopField)
               {
                  %k% := Object("base", RowBase, "table", oTable)
                  For   k2,v2 in oTable[k]
                  %k%.Insert(v2)
                  oFound.Insert(%k%)
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
   }
   else if MatchType = Containing+
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            Table_StringReplace(CurField, A_Space, "", A_Tab, "", "`n", "", "`r", "")
            if CurField contains %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if MatchType = Exactly+
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            Table_StringReplace(CurField, A_Space, "", A_Tab, "", "`n", "", "`r", "")
            if CurField in %StringsToSearch%
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   else if MatchType = StartingWith+
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            Table_StringReplace(CurField, A_Space, "", A_Tab, "", "`n", "", "`r", "")
            Loop, parse, StringsToSearch, "|"
            {
               if (SubStr(CurField,1,StrLen(A_LoopField)) = A_LoopField)
               {
                  %k% := Object("base", RowBase, "table", oTable)
                  For   k2,v2 in oTable[k]
                  %k%.Insert(v2)
                  oFound.Insert(%k%)
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
   }
   else if MatchType = EndingWith+
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            Table_StringReplace(CurField, A_Space, "", A_Tab, "", "`n", "", "`r", "")
            Loop, parse, StringsToSearch, "|"
            {
               if (SubStr(CurField, - (StrLen(A_LoopField) - 1)) = A_LoopField)
               {
                  %k% := Object("base", RowBase, "table", oTable)
                  For   k2,v2 in oTable[k]
                  %k%.Insert(v2)
                  oFound.Insert(%k%)
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
   }
   else if MatchType = RegEx
   {
      For k in oTable
      {
         if k is not integer   ;Rows are integers.
         continue
         Loop, parse, ColumnsToSearch, |
         {
            CurField := oTable[k][A_LoopField]
            if RegExMatch(CurField, StringsToSearch)   ; StringsToSearch = NeedleRegEx
            {
               %k% := Object("base", RowBase, "table", oTable)
               For   k2,v2 in oTable[k]
               %k%.Insert(v2)
               oFound.Insert(%k%)
               oFound.LastFound := oFound.LastFound "," k
            }
         }
      }
   }
   
   oFound.LastFound := LTrim(oFound.LastFound, ",")   ; stores row numbers of found rows in latest search
   return oFound
}

Table_mTable_HeaderToString(oTable, ColumnsDelimiter="") {   ; converts table's header (first row) to string
   if ColumnsDelimiter =
   ColumnsDelimiter := oTable.ColumnsDelimiter
   For k,v in oTable.ColumnNames
   Header .= v ColumnsDelimiter
   return RTrim(Header, ColumnsDelimiter)
}

Table_mTable_ToString(oTable, ColumnsDelimiter="", RowsDelimiter= "") {      ; converts table object to string
   if ColumnsDelimiter =
   ColumnsDelimiter := oTable.ColumnsDelimiter
   if RowsDelimiter =
   RowsDelimiter := oTable.RowsDelimiter

   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      RowString .= v2 ColumnsDelimiter
      RowString := RTrim(RowString, ColumnsDelimiter)
      TableString .= RowString RowsDelimiter
      RowString =
   }
   return RTrim(TableString, RowsDelimiter)
}

Table_mTable_ToListView(oTable) {   ; puts table object to ListView

   For k,v in oTable
   {
      if k is not integer   ;Rows are integers.
      continue
      For k2,v2 in oTable[k]
      {
         f%A_index% := v2
         TotalFields ++
      }   
      LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
      Loop, %TotalFields%
      f%A_index% =
      TotalFields =
   }
}



Table_mTable_Row2NumL(oTable, RowNumToSearch, Fields*) {   ; converts row identified by its fields to number but searches only through specified row numbers (limit). First matching. RowNumToSearch should be oFound.LastFound's value.
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      RowStringToSearch .= v
   }
   
   if (RowNumToSearch = "")   ; search through all rows
   return oTable.Row2Num(Fields*)
   
   Loop, parse, RowNumToSearch, |   ; search through specified rows
   {
      For k2,v2 in oTable[A_LoopField]
      RowString .= v2
      
      if (RowString = RowStringToSearch)
      return A_LoopField
      
      RowString =
   }
}

Table_mTable_LVSelInfo(oTable, RowNumToSearch="") {   ; gets info about selected rows in ListView
     Loop, %   LV_GetCount("Column") ; total number of columns in LV
   FromColumns .= A_Index "|"   
   FromColumns := RTrim(FromColumns,"|")
   
   ; oSelected structure: o1SelRow, o2SelRow, o3SelRow, etc.
   ; oSelRow structure: [1] oTableRowNum     [2] LVRowNum      [3,4,5 etc.] FieldsText
   oSelected := Object()
   Loop
    {
        RowNumber := LV_GetNext(RowNumber)
        if !RowNumber
        break
      
      oSelRow := A_Index
      %oSelRow% := Object()   ; oSelRow
      %oSelRow%.Insert("")   ; [1] oTable RowNum - dummy
      %oSelRow%.Insert(RowNumber)   ; [2] LV owNum
      
      oFields := Object()
      Loop, parse, FromColumns, |
      {
         LV_GetText(FieldText, RowNumber, A_LoopField)
         oFields.Insert(FieldText)
         %oSelRow%.Insert(FieldText)
      }
      %oSelRow%[1] := oTable.Row2NumL(RowNumToSearch, oFields*)    ; [1] oTable RowNum - real
      oSelected.Insert(%oSelRow%), oFields := ""
   }
    return oSelected
}

Table_mTable_LVDelete(oTable, RowNumToSearch="") {   ; deletes selected row from oTable and ListView. Deletes just 1. selected row for now.
   oSelected := oTable.LVSelInfo(RowNumToSearch)
   For k,v in oSelected   ; oSelected structure: o1SelRow, o2SelRow, o3SelRow, etc.
   {
      ; structure: [1] oTableRowNum     [2] LVRowNum      [3,4,5 etc.] FieldsText
      for k2,v2 in oSelected[k]
      {
         if A_index = 1      ; [1] oTableRowNum
         oTable.DeleteRow(v2)
         else if A_index = 2   ; [2] LVRowNum
         LV_Delete(v2)
         else
         break   ; [3,4,5 etc.] FieldsText - not relevant
      }
      return  oSelected.1      ; allow deleting just 1. selected row for now. Ignore other. Return info about deleted row.
   }
}

Table_mTable_LVAdd(oTable, Fields*) {   ; adds new row to oTable and ListView
   ; add to oTable
   oTable.AddRow(Fields*)   
   
   ; add to ListView
   TotalColumns := oTable.ColumnNames.MaxIndex()
   For k,v in Fields
   {
      if (A_index > TotalColumns)
      break
      f%A_index% := v
   }
   LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
}

Table_mTable_LVModify1(oTable, RowNumToSearch="") {   ; Returns row's to modify fields. Must be called prior to oTable.LVModify2()
   ; Relevant for machine: stores oTableRowNum and LVRowNum in oTable.LVModifyRowNums
   ; for faster performance when displaying search results in LV, specify oFound.LastFound as RowNumToSearch
   
   oSelected := oTable.LVSelInfo(RowNumToSearch)
   oFields := Object(), oRowNums := Object()

For k,v in oSelected.1   ; first selected row info
   {
      if A_index = 1      ; [1] oTableRowNum
      oRowNums.Insert(v)
      else if A_index = 2   ; [2] LVRowNum
      oRowNums.Insert(v)
      else   ; [3,4,5 etc.] FieldsText
      oFields.Insert(v)   
   }
   oTable.LVModifyRowNums := oRowNums   ; info used by oTable.LVModify2
   return oFields   ; return row's to modify fields
}   

Table_mTable_LVModify2(oTable, oNewFields) {   ; modifies row in ListView and oTable
   TableRowNum := oTable.LVModifyRowNums.1   ; [1] oTableRowNum
   LVRowNum := oTable.LVModifyRowNums.2   ; [2] LVRowNum

   oTable.ModifyRow(TableRowNum, oNewFields*)
   For k,v in oNewFields
   f%k% := v
   LV_Modify(LVRowNum,"",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
}

;====== oRow methods ======
Table_mRow_ToString(oRow, ColumnsDelimiter="") {   ; converts row object to string
   if ColumnsDelimiter =
   ColumnsDelimiter := oRow.table.ColumnsDelimiter
   For k,v in oRow
   {
      if k is not integer   ; field keys are integers.
      continue
      RowString .= v ColumnsDelimiter
   }
   return RTrim(RowString, ColumnsDelimiter)
}

Table_mRow_ToListView(oRow) {   ; puts row object to ListView
   For k,v in oRow
   {
      if k is not integer   ; field keys are integers.
      continue
      f%k% := v
   }
   LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
}
   

;====== Shared, other ======
Table_StringReplace(ByRef String, params*) {   ; replaces "param1" with "param2", "param3" with "param4" (etc.) in string
   For k,v in params
   {
      c++
      if c = 1
      {
         st := v
         continue
      }
      rt := v, c := 0
      StringReplace, String, String, %st%, %rt%, all
   }
}

SplitToObj(String,Delimiter="|") {
   obj := Object()
   Loop, parse, String, %Delimiter%
   obj.Insert(A_LoopField)
   return   obj
}





;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ExitApp
Pause::
Suspend
Pause,,1
return

Escape::
Suspend
ExitApp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

History:
- version 0.18 - release date 09.01.2011.
- version 0.15 - release date 29.12.2010.
- version 0.14 - release date 28.12.2010.
- version 0.13 - release date 28.12.2010.


Report this post
Top
 Profile  
Reply with quote  
PostPosted: November 17th, 2011, 1:50 pm 
Offline
User avatar

Joined: January 25th, 2006, 8:08 am
Posts: 225
Location: Froschtümpel
As I needed a simple table class as well, I took the latest version 0.18 from here and overhauled the existing sources to be more conform with AHK_L class syntax. I implemented all functionality available in the original source.

The current class is tested with AHK_L 1.1.5.1 on Win7 x64

Here are the sources for my version 0.1.0.0:

Code:
/*!
   Library: cTable version 0.0.1
      Simplified handling of tables
      
      Based on work by Learning one (see: http://www.autohotkey.com/forum/viewtopic.php?t=65995)
       
   Author: Hoppfrosch
*/

/*!
   Class: cTable
      Simplified handling of tables
*/
class cTable {
   static _version := "0.1.0.0"
   static ColumnNames := Object()
   static FilePath := ""
   static ColumnsDelimiter := "`t"
   static RowsDelimiter := "`n"
   static _debug := 0

   
   __New(InputVariableOrFile, ColumnsDelimiter="`t", RowsDelimiter= "`n", _debug=0) {
      this.__debug(_debug)
     
      this.ColumnsDelimiter := ColumnsDelimiter
      this.RowsDelimiter := RowsDelimiter
     
      if % (A_AhkVersion < "1.1.05.00" && A_AhkVersion >= "2.0") {
         MsgBox % "This class is only tested with AHK_L later than 1.1.05.00 (and before 2.0)`nAborting..."
         return
      }
         
      IfExist, %InputVariableOrFile%   ; construct table from file
      {
         oFile := FileOpen(InputVariableOrFile, "r `n", "UTF-8")   ; hard coded or option...?
         InputVariable := oFile.Read()
         oFile.Close()
         this.FilePath := InputVariableOrFile   ; store constructor file's FilePath
      }
      else   ; construct table from variable
         InputVariable := InputVariableOrFile

      oColumnNames := Object()

      Loop, parse, InputVariable, %RowsDelimiter%
      {
         CurRow := A_LoopField
         if A_index = 1   ; column names are specified as first row
         {
            Loop, parse, CurRow, %ColumnsDelimiter%
            {
               oColumnNames.Insert(A_LoopField)
               ColumnsCount++
            }
            this.ColumnNames := oColumnNames
            continue
         }
         RowNum := A_index-1
         %RowNum% :=  new cTableRow(CurRow, ColumnsCount, ColumnsDelimiter)
     
         this.Insert(%RowNum%)
      }
     
      return this
   }
   
   AddRow(Fields*) {   ; adds new row to table (to the bottom)
     
      NewRowNum := this.MaxIndex() + 1
      TotalColumns := this.ColumnNames.MaxIndex()
      %NewRowNum% := new cTableRow("",0,ColumnsDelimiter)
      For k,v in Fields
      {
         if (A_index > TotalColumns)
            break
         %NewRowNum%.Insert(v)
      }
      this.Insert(%NewRowNum%)
     
     
      return NewRowNum
   }
   
   Col2Num(ColumnsToSearch) {      ; converts column name(s) to column number(s)
     
     
      StringReplace, ColumnsToSearch, ColumnsToSearch, `,, `,`,, all
      StringReplace, ColumnsToSearch, ColumnsToSearch, |, `,, all
      For k,v in this.ColumnNames
      {
         if v in %ColumnsToSearch%
            Found .= k "|"
      }
      RetVal := RTrim(Found, "|")
     
     
      return RetVal
   }
   
   DeleteRow(RowToDeleteNumber="") {   ; deletes row(s)
     
      if (RowToDeleteNumber = 0)   ; delete all existing rows
      {
         this.Remove(1, this.MaxIndex())
         return
      }
      Else if (RowToDeleteNumber = "")   ; delete last row
      {
         oDeletedRow := Object()
         LastRN := this.MaxIndex()
         oDeletedRow := this[LastRN]
         this.Remove(LastRN)
         return oDeletedRow
      }
      else   ; delete specified row
      {
         oDeletedRow := Object()
         oDeletedRow := this[RowToDeleteNumber]
         this.Remove(RowToDeleteNumber)
         return oDeletedRow
      }
   }
   
   HeaderToString(ColumnsDelimiter="") {   ; converts table's header (first row) to string
      if ColumnsDelimiter =
         ColumnsDelimiter := this.ColumnsDelimiter
      For k,v in this.ColumnNames
         Header .= v ColumnsDelimiter
      RetVal := RTrim(Header, ColumnsDelimiter)
      return RetVal
   }
   
   InsertRow(NewRowNum ,Fields*) {   ; inserts new row in table
      if NewRowNum = 0   ; not allowed
         return
   
      if (NewRowNum > this.MaxIndex())
         NewRowNum := this.MaxIndex() + 1   ; add as last row
   
      %NewRowNum% := new cTableRow("",0,ColumnsDelimiter)
      TotalColumns := this.ColumnNames.MaxIndex()
      For k,v in Fields
      {
         if (A_index > TotalColumns)
            break
         %NewRowNum%.Insert(v)
      }
      this.Insert(NewRowNum, %NewRowNum%)
   }
   
   LVAdd(Fields*) {   ; adds new row to oTable and ListView
      ; add to oTable
      this.AddRow(Fields*)   
   
      ; add to ListView
      TotalColumns := this.ColumnNames.MaxIndex()
      For k,v in Fields
      {
         if (A_index > TotalColumns)
         break
         f%A_index% := v
      }
      LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
   }
   
   LVDelete(RowNumToSearch="") {   ; deletes selected row from oTable and ListView. Deletes just 1. selected row for now.
      oSelected := this.LVSelInfo(RowNumToSearch)
      For k,v in oSelected   ; oSelected structure: o1SelRow, o2SelRow, o3SelRow, etc.
      {
         ; structure: [1] oTableRowNum     [2] LVRowNum      [3,4,5 etc.] FieldsText
         for k2,v2 in oSelected[k]
         {
            if A_index = 1      ; [1] oTableRowNum
               this.DeleteRow(v2)
            else if A_index = 2   ; [2] LVRowNum
               LV_Delete(v2)
            else
               break   ; [3,4,5 etc.] FieldsText - not relevant
         }
         return  oSelected.1      ; allow deleting just 1. selected row for now. Ignore other. Return info about deleted row.
      }
   }
   
   LVModify1(RowNumToSearch="") {   ; Returns row's to modify fields. Must be called prior to oTable.LVModify2()
      ; Relevant for machine: stores oTableRowNum and LVRowNum in oTable.LVModifyRowNums
      ; for faster performance when displaying search results in LV, specify oFound.LastFound as RowNumToSearch
      oSelected := this.LVSelInfo(RowNumToSearch)
      oFields := Object()
      oRowNums := Object()

      For k,v in oSelected.1   ; first selected row info
      {
         if A_index = 1      ; [1] oTableRowNum
            oRowNums.Insert(v)
         else if A_index = 2   ; [2] LVRowNum
            oRowNums.Insert(v)
         else   ; [3,4,5 etc.] FieldsText
            oFields.Insert(v)   
      }
      this.LVModifyRowNums := oRowNums   ; info used by oTable.LVModify2
      return oFields   ; return row's to modify fields
   }
   
   LVModify2(oNewFields) {   ; modifies row in ListView and oTable
   
      TableRowNum := this.LVModifyRowNums.1   ; [1] oTableRowNum
      LVRowNum := this.LVModifyRowNums.2   ; [2] LVRowNum

      this.ModifyRow(TableRowNum, oNewFields*)
      For k,v in oNewFields
         f%k% := v
      LV_Modify(LVRowNum,"",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
   }

   LVSelInfo(RowNumToSearch="") {   ; gets info about selected rows in ListView
      Loop, %   LV_GetCount("Column") ; total number of columns in LV
         FromColumns .= A_Index "|"   
      FromColumns := RTrim(FromColumns,"|")
   
      ; oSelected structure: o1SelRow, o2SelRow, o3SelRow, etc.
      ; oSelRow structure: [1] oTableRowNum     [2] LVRowNum      [3,4,5 etc.] FieldsText
      oSelected := Object()
      Loop
      {
         RowNumber := LV_GetNext(RowNumber)
         if !RowNumber
            break
     
         oSelRow := A_Index
         %oSelRow% := Object()   ; oSelRow
         %oSelRow%.Insert("")   ; [1] oTable RowNum - dummy
         %oSelRow%.Insert(RowNumber)   ; [2] LV owNum
     
         oFields := Object()
         Loop, parse, FromColumns, |
         {
            LV_GetText(FieldText, RowNumber, A_LoopField)
            oFields.Insert(FieldText)
            %oSelRow%.Insert(FieldText)
         }
         %oSelRow%[1] := this.Row2NumL(RowNumToSearch, oFields*)    ; [1] oTable RowNum - real
         oSelected.Insert(%oSelRow%), oFields := ""
      }
      return oSelected
   }

   ModifyRow(RowToModifyNumber, Fields*) {   ; modifies row(s)
   
      oModifyedRow := new cTableRow("",0,ColumnsDelimiter)
      TotalColumns := this.ColumnNames.MaxIndex()
      For k,v in Fields
      {
         if (A_index > TotalColumns)
            break
         oModifyedRow.Insert(v)
      }
      if RowToModifyNumber = 0   ; modify all existing rows
      {
         Loop, % this.MaxIndex()
            this[A_Index] := oModifyedRow
      }
      else   ; modify specified row
         this[RowToModifyNumber] := oModifyedRow
   }
 
   NewFromScheme() {   ; creates new empty table from table object template
      oNewTable := new cTable("", this.ColumnsDelimiter, this.RowsDelimiter)
      oNewTable.ColumnNames := this.ColumnNames
      return oNewTable
   }
   
   Open() {   ; opens table's constructor file in Notepad if it exists
      FilePath := this.FilePath
      IfExist, %FilePath%
         Run, notepad "%FilePath%"
   }
   
   Reload() {   ; reads table's constructor file again and reconstructs table object if constructor file exists   
      FilePath := this.FilePath
      ColumnsDelimiter := this.ColumnsDelimiter,
      RowsDelimiter := this.RowsDelimiter
      IfNotExist, %FilePath%   ; if constructor file doesn't exist
         return   ; return and leave old table object as is
   
      this := new cTable(FilePath, ColumnsDelimiter,RowsDelimiter)
   
      return this   ; not necessary as first param is ByRef, but keep it
   }
   
   Row2Num(Fields*) {   ; converts row identified by its fields to number. First matching.
      TotalColumns := this.ColumnNames.MaxIndex()
      For k,v in Fields
      {
         if (A_index > TotalColumns)
            break
         RowStringToSearch .= v
      }
      For k,v in this
      {
         if k is not integer   ;Rows are integers.
            continue
         For k2,v2 in this[k]
            RowString .= v2
     
         if (RowString = RowStringToSearch) {
            return k
         }
     
         RowString =
      }
   }
   
   Row2NumL(RowNumToSearch, Fields*) {   ; converts row identified by its fields to number but searches only through specified row numbers (limit). First matching. RowNumToSearch should be oFound.LastFound's value.
      TotalColumns := this.ColumnNames.MaxIndex()
      For k,v in Fields
      {
         if (A_index > TotalColumns)
            break
         RowStringToSearch .= v
      }
   
      if (RowNumToSearch = "")   ; search through all rows
         return this.Row2Num(Fields*)
   
      Loop, parse, RowNumToSearch, |   ; search through specified rows
      {
         For k2,v2 in this[A_LoopField]
            RowString .= v2
     
         if (RowString = RowStringToSearch)
            return A_LoopField
     
         RowString =
      }
   }
   
   Save() {   ; converts table object to string and saves it to its constructor file. Use only if oTable is created from file.
      if (this.FilePath = "")
         return
      this.SaveAs(this.FilePath)
      return 1
   }

   SaveAs(FilePath, ColumnsDelimiter="`t", RowsDelimiter="`n") {   ; converts table object to string and saves it to specified file
      if ColumnsDelimiter =
         ColumnsDelimiter := this.ColumnsDelimiter
      if RowsDelimiter =
         RowsDelimiter := this.RowsDelimiter
      this.FilePath := FilePath
   
      FileContents := this.HeaderToString(ColumnsDelimiter) RowsDelimiter this.ToString(ColumnsDelimiter, RowsDelimiter)
   
      oFile := FileOpen(FilePath, "w `n", "UTF-8")   ; creates a new file, overwriting any existing file.
      oFile.Write(FileContents)
      oFile.Close()
   }
   
   Search(ColumnsToSearch, StringsToSearch, MatchType="containing") {   ; performs search through columns or whole table
      /* Parameters:
      ColumnsToSearch      "|" delimited list of columns to search. If empty (""), search through whole table (all columns.)
      StringsToSearch      "|" delimited list of strings to search except in RegEx MatchType.
      MatchType            Containing, Exactly, StartingWith, EndingWith, RegEx,  Containing+, Exactly+, StartingWith+, EndingWith+
                           "+" suffix means more permissive match type where spaces, tabs, newlines and carriage returns are not relevant for match.
      */
     
      oFound := this.NewFromScheme()   ; create empty table from this template

      If (ColumnsToSearch = "")      ; search through all columns - whole table.
      {
         For k,v in this.ColumnNames
            ColumnsToSearch .= A_Index "|"
         ColumnsToSearch := RTrim(ColumnsToSearch,"|")
      }
      else   ; search through specified columns
         ColumnsToSearch := this.Col2Num(ColumnsToSearch)
     
      If (SubStr(MatchType,0) = "+")   ; more permissive match type where spaces, tabs, newlines and carriage returns are not relevant for match.
         _cTable_StringReplace(StringsToSearch, A_Space, "", A_Tab, "", "`n", "", "`r", "")   ; like RegExReplace(StringsToSearch, "\s")

     
      if MatchType in Containing,Exactly,Containing+,Exactly+   ; prepare matchlist
      {
         StringReplace, StringsToSearch, StringsToSearch, `,, `,`,, all
         StringReplace, StringsToSearch, StringsToSearch, |, `,, all
      }
     
      ; About coding style below: longer way, but better performance due to less "if evaluations" in loop for MatchTypes not containing "+".
      if MatchType = Containing
      {
         For k in this
         {
            if k is not integer   ;Rows are integers.
               continue
            Loop, parse, ColumnsToSearch, |
            {
               CurField := this[k][A_LoopField]
               if CurField contains %StringsToSearch%
               {
                  oFound.Insert(this[k])
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
      else if MatchType = Exactly
      {
         For k in this
         {
            if k is not integer   ;Rows are integers.
               continue
            Loop, parse, ColumnsToSearch, |
            {
               CurField := this[k][A_LoopField]
               if CurField in %StringsToSearch%
               {
                  oFound.Insert(this[k])
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
      else if MatchType = StartingWith
      {
         For k in this
         {
            if k is not integer   ;Rows are integers.
               continue
            Loop, parse, ColumnsToSearch, |
            {
               CurField := this[k][A_LoopField]
               Loop, parse, StringsToSearch, "|"
               {
                  if (SubStr(CurField,1,StrLen(A_LoopField)) = A_LoopField)
                  {
                     oFound.Insert(this[k])
                     oFound.LastFound := oFound.LastFound "," k
                  }
               }
            }
         }
      }
      else if MatchType = EndingWith
      {
         For k in this
         {
            if k is not integer   ;Rows are integers.
               continue
            Loop, parse, ColumnsToSearch, |
            {
               CurField := this[k][A_LoopField]
               Loop, parse, StringsToSearch, "|"
               {
                  if (SubStr(CurField, - (StrLen(A_LoopField) - 1)) = A_LoopField)
                  {
                     oFound.Insert(this[k])
                     oFound.LastFound := oFound.LastFound "," k
                  }
               }
            }
         }
      }
      else if MatchType = Containing+
      {
         For k in this
         {
            if k is not integer   ;Rows are integers.
               continue
            Loop, parse, ColumnsToSearch, |
            {
               CurField := this[k][A_LoopField]
               _cTable_StringReplace(CurField, A_Space, "", A_Tab, "", "`n", "", "`r", "")
               if CurField contains %StringsToSearch%
               {
                  oFound.Insert(this[k])
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
      else if MatchType = Exactly+
      {
         For k in this
         {
            if k is not integer   ;Rows are integers.
               continue
            Loop, parse, ColumnsToSearch, |
            {
               CurField := this[k][A_LoopField]
               _cTable_StringReplace(CurField, A_Space, "", A_Tab, "", "`n", "", "`r", "")
               if CurField in %StringsToSearch%
               {
                  oFound.Insert(this[k])
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
      else if MatchType = StartingWith+
      {
         For k in this
         {
            if k is not integer   ;Rows are integers.
               continue
            Loop, parse, ColumnsToSearch, |
            {
               CurField := this[k][A_LoopField]
               _cTable_StringReplace(CurField, A_Space, "", A_Tab, "", "`n", "", "`r", "")
               Loop, parse, StringsToSearch, "|"
               {
                  if (SubStr(CurField,1,StrLen(A_LoopField)) = A_LoopField)
                  {
                     oFound.Insert(this[k])
                     oFound.LastFound := oFound.LastFound "," k
                  }
               }
            }
         }
      }
      else if MatchType = EndingWith+
      {
         For k in this
         {
            if k is not integer   ;Rows are integers.
               continue
            Loop, parse, ColumnsToSearch, |
            {
               CurField := this[k][A_LoopField]
               _cTable_StringReplace(CurField, A_Space, "", A_Tab, "", "`n", "", "`r", "")
               Loop, parse, StringsToSearch, "|"
               {
                  if (SubStr(CurField, - (StrLen(A_LoopField) - 1)) = A_LoopField)
                  {
                     oFound.Insert(this[k])
                     oFound.LastFound := oFound.LastFound "," k
                  }
               }
            }
         }
      }
      else if MatchType = RegEx
      {
         For k in this
         {
            if k is not integer   ;Rows are integers.
               continue
            Loop, parse, ColumnsToSearch, |
            {
               CurField := this[k][A_LoopField]
               if RegExMatch(CurField, StringsToSearch)   ; StringsToSearch = NeedleRegEx
               {
                  oFound.Insert(this[k])
                  oFound.LastFound := oFound.LastFound "," k
               }
            }
         }
      }
     
      oFound.LastFound := LTrim(oFound.LastFound, ",")   ; stores row numbers of found rows in latest search
      return oFound
   }

   StringReplace(params*) { ; replaces "param1" with "param2", "param3" with "param4" (etc.) in all fields in table object
      For k,v in this
      {
         if k is not integer   ;Rows are integers.
            continue
         this[k] =  this[k].StringReplace(params)
      }
   }

   ToString(ColumnsDelimiter="", RowsDelimiter= "") {      ; converts table object to string
      if ColumnsDelimiter =
         ColumnsDelimiter := this.ColumnsDelimiter
      if RowsDelimiter =
         RowsDelimiter := this.RowsDelimiter

      MyCount := this.MaxIndex()
      Loop, %MyCount%
      {
         Row := this[A_Index]
         RowString .= Row.ToString()
         if (A_Index < this.MaxIndex() )
            RowString .= RowsDelimiter
      }
      RetVal := RowString
      return RetVal
   }
   
   ToListView() {   ; puts table object to ListView
      For k,v in this
      {
         if k is not integer   ;Rows are integers.
            continue
         For k2,v2 in this[k]
         {
            f%A_index% := v2
            TotalFields ++
         }   
         LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
         Loop, %TotalFields%
         f%A_index% =
         TotalFields =
      }
   }

   Version() {
      return this._version
   }
   
}


/*!
   Class: cTableRow
      Base class for handling table rows
*/
class cTableRow {
      /**************************************************************************************************************
   Variable: version
   Version of class implementation
   ***************************************************************************************************************   
   */
   static _version := "0.1.0"
   static ColumnsDelimiter := "`t"
   static _debug := 0

   Version() {
      return this._version
   }
   
   
   __New(CurRow="", ColumnsCount=0, ColumnsDelimiter="`t", _debug=0) {
      this.__debug(_debug)
     
      StringSplit, field, CurRow, %ColumnsDelimiter%
      Loop, %ColumnsCount%
         this.Insert(field%A_Index%)
      Loop, %ColumnsCount%
         field%A_Index% =
     
     
      return this
   }
   
   ToString(ColumnsDelimiter="") {   ; converts row object to string
     
      if ColumnsDelimiter =
         ColumnsDelimiter := this.ColumnsDelimiter
      Cols := this.MaxIndex()
      Loop, %Cols%
      {
         RowString .= this[A_Index]
         if (A_Index < this.MaxIndex() )
            RowString .= ColumnsDelimiter
      }
      RetVal:= RowString
      return RetVal
   }
   
   ToListView() {   ; puts row object to ListView
      For k,v in this
      {
         if k is not integer   ; field keys are integers.
            continue
         f%k% := v
      }
      LV_Add("",f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30)
   }
     
   StringReplace(params*) { ; replaces "param1" with "param2", "param3" with "param4" (etc.) in all fields in table object
      For k,v in this   ; v = field
      {
         c := 0
         For k2,v2 in params[1]
         {
            c++
            if c = 1
            {
               st := v2
               continue
            }
            rt := v2, c := 0
            StringReplace, v, v, %st%, %rt%, all
         }
         this[k] := v
      }
      return this
   }
}


;====== Shared, other ======
_cTable_SplitToObj(String,Delimiter="|") {
   obj := Object()
   Loop, parse, String, %Delimiter%
      obj.Insert(A_LoopField)
   return   obj
}

_cTable_StringReplace(ByRef String, params*) {   ; replaces "param1" with "param2", "param3" with "param4" (etc.) in string
   For k,v in params
   {
      c++
      if c = 1
      {
         st := v
         continue
      }
      rt := v, c := 0
      StringReplace, String, String, %st%, %rt%, all
   }
}

/*
Author: IsNull
http://www.autohotkey.com/forum/topic59244.html
license: not specified
default license for forum where initially posted:GPL v2
*/
_cTable_ToString(this){
   if(!IsObject(this))
      return, this
   str := ""
   fields := this._NewEnum()
   while fields[k, v]
   {
      if(!IsObject(v)){
         str .= !IsFunc(v) ? "[" k "] := "  v "`n" : k "() calls "  v "`n"
      }else{
         subobje := _cTable_ToString(v)
         str .= "[" k "]<ob>`n" _cTable_multab(subobje)
      }
   }
   return, str
}

_cTable_multab(str){
   Loop, parse, str, `n, `r
      newstr .= A_Tab A_LoopField "`n"
   return, newstr
}


And here is a example file (pretty similar to original):

Code:
#include cTable.ahk

;===oTable testing area=================================================================
Variable =      ; column names are specified as first row
(
First name%A_Tab%Last name%A_Tab%Occupation%A_Tab%Notes
Jack%A_Tab%Gates%A_Tab%Driver%A_Tab%
Mark%A_Tab%Weber%A_Tab%Student%A_Tab%His father is a driver.
Jim%A_Tab%Tucker%A_Tab%Driver%A_Tab%
Jill%A_Tab%Lochte%A_Tab%Artist%A_Tab%Jack's sister.
Jessica%A_Tab%Hickman%A_Tab%Student%A_Tab%
Mary%A_Tab%Jones%A_Tab%Teacher%A_Tab%Her favorite song is "D r i v e r".
Lenny%A_Tab%Stark%A_Tab%Driver%A_Tab%
Jack%A_Tab%Black%A_Tab%Actor%A_Tab%His wife is artist.
Tony%A_Tab%Jackman%A_Tab%Surfer%A_Tab%
Jonny%A_Tab%Poor%A_Tab%Beggar%A_Tab%
Scott%A_Tab%Jenstan%A_Tab%Teacher%A_Tab%Lives in New York.
)


oTable := new cTable(Variable)   ; creates table object from variable
Variable =    ; not longer needed



; create simple gui for testing
Gui 1: Add, ListView, x5 y5 w500 h300 grid, % oTable.HeaderToString("|")   ; converts table's header (first row) to string.
Gui 1: Show, w510 h310 hide


;=== Field management ===
;MsgBox % oTable.3.2   ; get value from [3. row, 2. column]
;oTable.1.1 := "Bobby"   ; set [1. row, 1. column] to "Bobby"


;=== Converting to numbers ===
;MsgBox % oTable.Row2Num("Jonny", "Poor", "Beggar")   ; get number of row whose fields are: Jonny, Poor, Beggar. (Identification by fields)
;MsgBox % oTable.Col2Num("Occupation")   ; get number of "Occupation" column
;MsgBox % oTable.Col2Num("First name|Notes")   ; get numbers of "First name" and "Notes" columns


;=== ToString method ===
;MsgBox % oTable.ToString()   ; convert whole table object to string
;MsgBox % oTable.3.ToString()   ; convert 3. row object to string
;MsgBox % oTable.11.ToString("#")   ; convert 11. row object to string but use custom delimiter
;MsgBox % oTable.HeaderToString()   ; converts table's header (first row) to string


;=== MaxIndex (Count) ===
;MsgBox % oTable.MaxIndex()   ; get total number of rows (in future maybe: oTable.Count)
;MsgBox % oTable.ColumnNames.MaxIndex()   ; get total number of columns


;=== Row management ===
;oTable.AddRow("Joe", "Newman", "Kiteboarder", "Freestyle & Wave")   ; add row (to the bottom)
;oTable.InsertRow(2 ,"Mike", "Insertovich", "Actor")   ; inserts new row number 2.
;oTable.ModifyRow(3 ,"Sergey", "Modifysky", "Actor")   ; modify row number 3.
;oTable.ModifyRow(0 ,"Chris", "Allman", "Actor")   ; modify all existing rows
;oTable.DeleteRow(2)   ; delete 2. row
;oTable.DeleteRow()      ; delete last row
;oTable.DeleteRow(0)   ; delete all rows


;=== Searching ===
;MsgBox % oTable.Search("Occupation", "Driver").ToString()    ; search Occupation column for containing string "driver"
;MsgBox % oTable.Search("Occupation|Notes", "Driver").ToString()    ; search Occupation and Notes columns for containing string "driver"
;MsgBox % oTable.Search("", "Driver", "containing+").ToString()    ; search whole table (all columns) for containing string "driver" but ignore withespaces
;MsgBox % oTable.Search("Last name", "^J.*an$", "RegEx").ToString() ; Search for all last names starting with "J" and ending with "an".
;MsgBox % oTable.Search("First name", "ny", "EndingWith").ToString()    ;  search first names ending with "ny"
;MsgBox % oTable.Search("Last name", "ja|bla", "StartingWith").ToString()    ;  search last names starting with "ja" or "bla"
;MsgBox % oTable.Search("", "Jack", "exactly").ToString()    ; search whole table (all columns) for string "Jack" (not containing, but exactly)


;=== Multiple filters search ===
; step 1: search "Occupation" and "Notes" columns for containing "Driver" or "artist" strings. "|" is query delimiter.
; step 2: search that search result again: search "First name" column for containing "J" string
;oFound := oTable.Search("Occupation|Notes", "Driver|artist")      ; store search results as object
;oFound2 := oFound.Search("First name", "J")      ; search oFound (second search filter)

; or shorter:   oFound2 := oTable.Search("Occupation|Notes", "Driver|artist").Search("First name", "J")   ; etc. --> multiple filters

;MsgBox % oFound2.ToString()      ; convert search results to string
;MsgBox % oFound2.2.ToString()   ; convert 2. row from search results to string
;MsgBox % oFound2.1.3   ; get [1. row, 3. column] field from search results
;MsgBox % oFound2.MaxIndex()   ; get number of found rows - from oFound2 (in future maybe: oFound2.Count)


;=== RegEx search ===   and LastFound property
; Search for all last names starting with "J" and ending with "an"
;oFound3 := oTable.Search("Last name", "^J.*an$", "RegEx")   ; store search results as object
;MsgBox % oFound3.ToString()   ; convert search results to string
;MsgBox % oFound3.LastFound   ; each time after calling search method, row numbers of found rows are stored in LastFound key/property - read only.


;=== Massive StringReplace ===
;oTable.StringReplace("y", "X", ".", " :)")   ; replaces "param1" with "param2", "param3" with "param4" (etc.) in all fields in table object.


;=== Interacting with files ===
;oTable.SaveAs(A_ScriptDir "\Table file.txt","|","#")      ; converts table object to string and saves it to specified file but uses custom delimiters.
; When constructing table from such file, must specify custom delimiters -->  oTable := Table_ObjCreate(A_ScriptDir "\Table file.txt","|","#")

;oTable1 := new cTable(A_ScriptDir "\Table file.txt")   ; creates table object from constructor file
;oTable1.Save()   ; converts table object to string and saves it to its constructor file. Use only if oTable is created from file.
;oTable.SaveAs(A_ScriptDir "\Table file.txt")      ; converts table object to string and saves it to specified file.
;oTable.Open()   ; opens table's constructor file in Notepad if it exists
;oTable.Reload()   ; reads table's constructor file again and reconstructs table object if constructor file exists



;=== ToListView method ===
F1::   ; shows whole oTable in ListView
LV_Delete()   ; empty ListView
oTable.ToListView()
Gui 1: Show
Return


F2::   ; finds all drivers and shows search results in ListView
LV_Delete()   ; empty ListView
oTable.Search("Occupation", "driver").ToListView()
Gui 1: Show
Return


F3::   ; Do a RegEx search and show search results in ListView
LV_Delete()   ; empty ListView
oFound := oTable.Search("Last name", "^J.*an$", "RegEx") ; Search for all last names starting with "J" and ending with "an".
oFound.ToListView()
Gui 1: Show
;MsgBox, 64, Numbers of found rows in latest search, % oFound.LastFound   ; LastFound property
;MsgBox, 64, oFound to string, % oFound.ToString()
Return


F4::   ; search whole table (all columns) for containing string "new" and show search results in ListView
LV_Delete()   ; empty ListView
oTable.Search("","New").ToListView()   ; empty 1. parameter means: search through whole table (all columns)
;oTable.1.ToListView()   ; adds 1. row from oTable to ListView
Gui 1: Show
return



 
;=== oTable - ListView interaction ===
d::oTable.LVDelete()   ; delete selected row
a::oTable.LVAdd("Mia","Addstan","Driver")   ; add new row

e::      ; edit selected row
CurFields =
oCurFields := oTable.LVModify1()   ; returns row's to modify fields. Must be called prior to oTable.LVModify2()
for k,v in oCurFields
CurFields .= v "|"
CurFields := RTrim(CurFields,"|")

if (CurFields = "")   ; nothing selected
return

InputBox, NewRow,, Current Fields:  %CurFields%,,,,,,,,%CurFields%
if ErrorLevel
return

oNewFields := _cTable_SplitToObj(NewRow)
oTable.LVModify2(oNewFields)   ; modifies row in ListView and oTable
return


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ExitApp
Pause::
Suspend
Pause,,1
return

GuiClose:
Escape::
Suspend
ExitApp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

I published my work - containing a test suite for the class as well - on Github

Have fun ...



_________________________

Code:
;     (.)~(.)   
;    (-------)                                   
;---ooO-----Ooo---------------------------------------------------
;    Hoppfrosch  - AHK 1.1.05.01 Unicode 32bit on Win7 Ultimate
;-----------------------------------------------------------------                       
;    ( )   ( )                           
;    /|\   /|\ 


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 23rd, 2011, 9:50 pm 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
That was my first custom object :mrgreen: , while class syntax wasn't available yet. I'm glad you fond time to rewrite it in class syntax style. :wink: If you have time and interest, I suggest you to implement InvertTable method. This [VxE]'s function, which is made for string based table (delimiters: row `n, column `t), not 2D array like cTable, shows what I mean;
Code:
; ======Example======
Table=
(
A`tB
1`t4
2`t5
3`t6
)

F1::   ; press F1 to invert and re-invert table
Table := InvertTable(Table)
MsgBox, %Table%
return

; ======Function======
InvertTable( Table ) { ; by [VxE]   http://www.autohotkey.com/forum/topic66290.html
   Loop, Parse, Table, `n, `r
      If ( 1 == B_Index := A_Index )
         Loop, Parse, A_LoopField, % Table := "`t"
            cell_%B_Index%_%A_Index% := A_LoopField, C_Index := A_Index
      Else Loop, Parse, A_LoopField, % "`t"
            cell_%B_Index%_%A_Index% := A_LoopField

   Loop, % C_Index
      Loop % B_Index + 0 * ( C_Index := A_Index )
         Table .= Chr( 9 + ( A_Index = 1 ) ) . cell_%A_Index%_%C_Index%

   Return SubStr( Table, 3 )
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 25th, 2012, 9:45 am 
Offline
User avatar

Joined: January 25th, 2006, 8:08 am
Posts: 225
Location: Froschtümpel
Learning one wrote:
If you have time and interest, I suggest you to implement InvertTable method.


Mathematically spoken this is "transpose a matrix". "Invert" seems to me to be an incorrect term in this context, since matrix inversion has an exact mathematical meaning ...

I'm not quite sure how to handle transposing correctly in my context: as in original data the first row has some special handling/meaning, as it holds the column descriptions ... Should I transpose the whole matrix (including special handled first row) - or should I omitt the first row? After transposing: Should the new first new row be handled as special "title row" - or should it be shifted to second row to be a simple data row? As I don't have an idea now, what is "the correct way" (if there is any at all ...), I hestiate to implement it ...


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 21 posts ]  Go to page Previous  1, 2

All times are UTC [ DST ]


Who is online

Users browsing this forum: Aravind, sks, Stigg and 12 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group