AutoHotkey Community

It is currently May 27th, 2012, 12:01 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: [object] Table
PostPosted: December 10th, 2010, 10:18 pm 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
:arrow: Update 17.11.2011.: Latest version by hoppfrosch here.



______________________
Original post: (obsolete code)
I'm not sure is this for Scripts & Functions or Ask for Help forum. Anyway, this is the first time I tried to code a object for AHK_L, so I would like to hear:
- Am I doing it right?
- Am I going in right direction?
- other comments and suggestions.

Main purpose of object is to search the columns in a database for a specified string. Can set/get cells, do a search through multiple columns, add rows, etc. Not finished, just a prerelease. Here is what I have done for now;

oTable.ahk
Code:
;===Description=========================================================================
; [object] Table - prerelease    by Learning one


;===Functions===========================================================================
Table_ObjCreate(FilePath,ColumnNames, ColumnsDelimiter="`t", RowsDelimiter= "`n") {   ; creates object and its methods
   static base := Table_Base("SetCell GetCell Save SaveAs Open Reload Search SearchColumn ChangeDelimiters Get Set Add Replace", "Table_m")
   obj := Object("base", base)
   
   IfNotExist, %FilePath%
   FileAppend, , %FilePath%, UTF-8
   else
   {
      oFile := FileOpen(FilePath, "r `n", "UTF-8")
      obj.c := oFile.Read()
      oFile.Close()
   }
   obj.cn := ColumnNames, obj.fp := FilePath
   obj.cd := ColumnsDelimiter, obj.rd := RowsDelimiter
   return obj
}

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

Table_mGetCell(obj, ColumnName, RowNumber) {
   ColumnsDelimiter := obj.cd, RowsDelimiter := obj.rd, ColumnNames := obj.cn
   Var := obj.c
   
   ColIndex := Table_ColumnNames2Num(obj ,ColumnName)
   if ColIndex =
   return
   Loop, parse, var, %RowsDelimiter%
   {
      if (A_Index = RowNumber)
      {   
         CurRow := A_LoopField
         Loop, parse, CurRow, %ColumnsDelimiter%
         {
            if (A_Index = ColIndex)
            return A_LoopField
         }
      }
   }
}

Table_mSetCell(obj, ColumnName, RowNumber, NewCellContents, Save=1) {
   ColumnsDelimiter := obj.cd, RowsDelimiter := obj.rd, ColumnNames := obj.cn
   Var := obj.c
   
   ColIndex := Table_ColumnNames2Num(obj ,ColumnName)
   if ColIndex =
   return
   Loop, parse, var, %RowsDelimiter%
   {
      if (A_Index = RowNumber)
      {   
         CurRow := A_LoopField
         Loop, parse, CurRow, %ColumnsDelimiter%
         {
            if (A_Index = ColIndex)
            NewCurRow .= NewCellContents ColumnsDelimiter
            else
            NewCurRow .= A_LoopField ColumnsDelimiter
         }
         NewCurRow := Trim(NewCurRow, ColumnsDelimiter)
         NewTableContents .=  NewCurRow RowsDelimiter
      }
      else
      NewTableContents .= A_LoopField RowsDelimiter
   }
   
   NewTableContents := Trim(NewTableContents, RowsDelimiter)
   obj.c := NewTableContents
   if Save
   obj.Save()
}

Table_mSearchColumn(obj, ColumnsToSearch, StringToSearch) {
   ColumnsDelimiter := obj.cd, RowsDelimiter := obj.rd, ColumnNames := obj.cn
   Var := obj.c
   
   ColNumbersToSearch := Table_ColumnNames2Num(obj ,ColumnsToSearch)
   if ColNumbersToSearch =
   return

   Loop, parse, var, %RowsDelimiter%
   {
      CurRow := A_LoopField
      if CurRow is space
      continue
      Loop, parse, CurRow, %ColumnsDelimiter%
      {
         if A_Index in %ColNumbersToSearch%
         {
            if A_LoopField contains %StringToSearch%
            FoundRowsList .= CurRow RowsDelimiter
         }
      }
   }
   Return Trim(FoundRowsList, RowsDelimiter)
}

Table_mSearch(obj, StringToSearch) {
   ColumnsDelimiter := obj.cd, RowsDelimiter := obj.rd, ColumnNames := obj.cn
   Var := obj.c
   
   Loop, parse, var, %RowsDelimiter%
   {
      if A_LoopField contains %StringToSearch%
      FoundRowsList .= A_LoopField RowsDelimiter
   }
   Return Trim(FoundRowsList, RowsDelimiter)
}

Table_mSave(obj) {
   Contents := obj.c, FilePath := obj.fp
   oFile := FileOpen(FilePath, "w `n", "UTF-8")   ; creates a new file, overwriting any existing file.
   oFile.Write(Contents)
   oFile.Close()
}

Table_mSaveAs(obj,NewFilePath) {
   Contents := obj.c
   oFile := FileOpen(NewFilePath, "w `n", "UTF-8")   ; creates a new file, overwriting any existing file.
   oFile.Write(Contents)
   oFile.Close()
}

Table_mOpen(obj) {
   FilePath := obj.fp
   Run, notepad "%FilePath%"
}

Table_mReload(obj) {
   FilePath := obj.fp
   oFile := FileOpen(FilePath, "r `n", "UTF-8")
   obj.c := oFile.Read()
   oFile.Close()
}

Table_mChangeDelimiters(obj, NewColumnsDelimiter, NewRowsDelimiter, Save=1) {
   ColumnsDelimiter := obj.cd, RowsDelimiter := obj.rd, ColumnNames := obj.cn
   Var := obj.c
   StringReplace, var, var, %ColumnsDelimiter%, %NewColumnsDelimiter%, all
   StringReplace, var, var, %RowsDelimiter%, %NewRowsDelimiter%, all
   obj.c := var
   obj.cd := NewColumnsDelimiter, obj.rd := NewRowsDelimiter
   if Save
   obj.Save()
}

Table_mGet(obj) {
   return obj.c
}

Table_mSet(obj, NewTableContents, Save=1) {
   obj.c := NewTableContents
   if Save
   obj.Save()
}

Table_mAdd(obj, NewRow, Save=1) {
   RowsDelimiter := obj.rd, Contents := obj.c
   NewRow := Trim(NewRow, RowsDelimiter)
   obj.c := Contents RowsDelimiter NewRow
   if Save
   obj.Save()
}

Table_mReplace(obj, SearchText, ReplaceText="", Save=1) {
   Var := obj.c
   StringReplace, Var, Var, %SearchText%, %ReplaceText%, All
   obj.c := var
   if Save
   obj.Save()
   return ErrorLevel
}

Table_ColumnNames2Num(obj, ColumnsToSearch) {
   ColumnNames := obj.cn
   Loop, parse, ColumnsToSearch, |
   {
      CurColName := A_LoopField
      Loop, parse, ColumnNames, |
      {
         if (A_LoopField = CurColName)
         Found .= A_Index ","
      }
   }
   return Trim(Found, ",")
}


Testing script:
Code:
#Include, oTable.ahk

;===Auto-execute========================================================================
FilePath := A_ScriptDir "\MyTable.txt"
IfNotExist, %FilePath%
Gosub, CreateSampleFile

oTable := Table_ObjCreate(FilePath, "First name|Last name|Occupation|Notes")   ; create table object from file, define column names
return


;===Hotkeys for testing==================================================================
F1::MsgBox, % oTable.SearchColumn("Occupation","Driver")   ; search for "Driver" (match only in "occupation" column)
F2::MsgBox, % oTable.SearchColumn("First name|Last name","Jack")   ; search for "Jack" (match  in "First name" and "Last name" columns)
F3::MsgBox, % oTable.Search("Driver")   ; search for "driver" (match anywhere)

F4::MsgBox, % oTable.Get() ; get object's contents and show it in MsgBox
F5::oTable.Open()   ; open object's FilePath in notepad
F6::oTable.Add("Mary" A_Tab   "Gills" A_Tab "Nurse" A_Tab)   ; adds a new row

F7::MsgBox, % oTable.GetCell("Last name",4)   ; get value from ["last name" column, 4. row]
F8::oTable.SetCell("First name",1,"Bobby")   ; set ["first name" column, 1. row] to value "Bobby"


/*;===some other examples===
oTable.Reload()   ; reads object's file again and stores it in object's contents
oTable.Save()   ; saves object's contents in its file
oTable.SaveAs(A_ScriptDir "\MyTable backup.txt")   ; saves object's contents in new file (make a backup)
oTable.ChangeDelimiters("|","#")   ; change columns and rows delimiters
*/


;===Subroutines=========================================================================
CreateSampleFile:
SampleFileContents =
(
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%
Jessica%A_Tab%Hickman%A_Tab%Student%A_Tab%
Mary%A_Tab%Jones%A_Tab%Teacher%A_Tab%Her favorite song is "Driver"
Lenny%A_Tab%Stark%A_Tab%Driver%A_Tab%
Jack%A_Tab%Black%A_Tab%Actor%A_Tab%
Tony%A_Tab%Jackman%A_Tab%Surfer%A_Tab%
Jonny%A_Tab%Poor%A_Tab%Beggar%A_Tab%
)

oFile := FileOpen(FilePath, "w `n", "UTF-8")
oFile.Write(SampleFileContents)
oFile.Close()
return



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

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


Last edited by Learning one on January 25th, 2012, 9:02 pm, edited 5 times in total.

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

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
3 days passed, 90 views, no replies. As I said, this is the first time I tried to code a object for AHK_L, so a really need some critics (both positive and negative) before I go on...


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 13th, 2010, 10:27 pm 
Learning one wrote:
3 days passed, 90 views, no replies. As I said, this is the first time I tried to code a object for AHK_L, so a really need some critics (both positive and negative) before I go on...


can't help you out, but i really wish there was some type of standardized way/documentation of dealing with objects in AHK_L, seems like there is a lot of confusion and i certainly have no resolution


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: December 13th, 2010, 10:49 pm 
Quote:
... really need some critics (both positive and negative) before I go on...

OK - you presented your object in somewhat of a cunfusing manner. An object (specifically one that has class-like definition) is somewhat like a function library - I'd suggest presenting the library separate from the example usage (ex. [AHK_L] Arrays)

Also, you might want to mention that you can just run the script - you don't have to create an external file - since the script does that for you.

Otherwise, looks good (haven't tried it though :wink: ). Personally I like to group my object together with brackets, and indent the methods (but that's just code appearance).


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: December 13th, 2010, 11:04 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Code:
   obj.cn := ColumnNames, obj.fp := FilePath
   obj.cd := ColumnsDelimiter, obj.rd := RowsDelimiter

could be done shorter as
Code:
   obj := Object("base", base, "cn", ColumnNames, "fp", FilePath, cd .....)



I also don't see a reson for

Code:
   Contents := obj.c, FilePath := obj.fp
   oFile := FileOpen(FilePath, "w `n", "UTF-8")   ; creates a new file, overwriting any existing file.
   oFile.Write(Contents)
   oFile.Close()
 


When it coudl be done with object references in the first place. You should fetch object fields into local variables only if they are used repeatdly, altho I think this level of optimisation is usually not necessary.

So, above should definitelly be:

Code:
f := FileOpen(Fobj.fp, "w `n", "UTF-8"), f.Write(obj.c), f.Close()


This is very apparent in your code like:

Code:
  Var := obj.c
   StringReplace, Var, Var, %SearchText%, %ReplaceText%, All
   obj.c := var


This is not very mutch in OO spirit but it might has its merrits in AHK due to simplicity:
Code:
oTable.Add("Mary" A_Tab   "Gills" A_Tab "Nurse" A_Tab)


In OO inteface, table should have row objects. The semantically valid way would be to crate row object, populate it, and then add it:

Code:
   oRow := oTable.NewRow() ;returns row object with the same scheme as table
   oRow.field[1] := "Mary", oRow.field[2] := "Gillls", oRow.field[3] := "Nurse"
  ; or oRow.AddFields("Mary", "Gills", "Nurse")
   oTable.AddRow(oRow)
  ; or much simpler:
   oTable.AddRow( oTable.NewRow().AddFields("Mary", "Gills", "Nurse") )
  ; or even simpler
   oTable.AddRow("Mary", "Gills", "Nurse")   
   ; makes row object and populate its fields with given strings, then adds row object to the table



In
Code:
Table.SetCell("First name",1,"Bobby")   ; set ["first name" column, 1. row] to value "Bobby"

It would be probably better if you could use:

Code:
 oTable[1,"First name"] := "Bobby"

or more verbose
Code:
 oRow := oTable[1]
 oRow["First Name"] := "Bobby"


Those are just some quick thoughts based on how actual OO libraries around are usually implemented.
Now, given the fact that AHKL is not OO language stricty speaking some of those might be harder to implement then in mainstreem languages so even in this state its not that bad. The most important note is the last one - you are changing specific object so the change should be going trough it, not trough its "class method" how its usually called in pure OO langs.

At the end, I don't think that file is necessary - you should work with string only and if user needs to save to file he can do it in one line of code. It also limits the usability of the "class"

_________________
Image


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

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Thank you for feedback. :)

@majkinetor: regarding rows as objects - should table object be constructed like this?
Code:
Table object members
   ; Properties
      ColumnsDelimiter, RowsDelimiter, FilePath, Encoding, etc.
   ; Methods
      SearchColumn, Save, SaveAs, Open, Reload, etc.
   
   Rows object members
      ; Properties
         1 ; 1. row contents
         2 ; 2.  row contents
         ; etc (row object as array)
      ; Methods
         Insert      ; (built in AHK_L)
         Remove      ; (built in AHK_L)

Code:
; syntax examples
var := oTable.Rows.7            ; get 7. row contents
oTable.Rows.7 := "seven"      ; set 7. row contents


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 14th, 2010, 7:50 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Code:
oTable.Rows.7 := "seven"

is incorrect because Rows.7 means 7th row, which is row object, not a field.

So, it should be

Code:
   oTable.Rows.7.1 := "seven.1"
   oTable.Rows.7.2 := "seven.2" ;should rise an error if there is no 2nd column, so "field" should be "property" with getter and setter functions.
 msgbox % oTable.Rows.Count


Quote:
ColumnsDelimiter, RowsDelimiter

This is redudant. ColumnDelimiter is RowDelimiter once you adopt the convention that column names are specified as first row.

Quote:
Rows object members
; Properties
1 ; 1. row contents
2 ; 2. row contents


Ideally, its represented as two properties:
row[1] or row["Coloumn 1 Name"]

Again, I don't think you need to work with files. At the end, table objects usually have extra functions like Writeto(FileName, encoding) or LoadFrom(FileName) so those don't need to be part of interface. The only reason would be real time savings handled by object itself but thats not very frequent operation.

SearchColumn is perhaps better as Search with aditional parameter (search direction). This is how its done in VBA for Excel for example.

Rows usually have Append & Insert functions along with Delete.
You should also have an option to add/remove Column (or at least append)

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 14th, 2010, 8:08 pm 
Offline

Joined: November 7th, 2006, 9:47 pm
Posts: 1934
Location: Germany
Learning one wrote:
3 days passed, 90 views, no replies.

Thats nothing against this: http://www.autohotkey.com/forum/viewtopic.php?p=249830#249830

werD wrote:
You want replies post broken script Wink you want views post a good one


... sorry for beeing off-topic.

_________________
{1:"ahkstdlib", 2:"my libs", 3:"my apps", 4:"my license"}
--> Don't feed the troll! <--


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 14th, 2010, 9:29 pm 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
@majkinetor, I can't figure out how to create fields like
Quote:
oTable.Rows.7.1
oTable.Rows.7.2
etc.
EDIT: I figured it out :D

@Tuncay: that's really very demotivating, especially fincs' first reply :lol:


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

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Here is the beginning of new construction - rows as objects, fields as elements. I guess it's more in OO spirit now. Syntax:
Code:
MsgBox % oTable.Rows.3.2   ; get value from [3. row, 2. column]
oTable.Rows.1.1 := "Bobby"   ; set [1. row, 1. column] to "Bobby"

I'm unsure about SearchColumn method. Should I return search results as:
- object - current solution in code below - sounds more "proper" but more complicated - not in AHK simplicity spirit
- string - simpler solution for a user
Code:
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%
Jessica%A_Tab%Hickman%A_Tab%Student%A_Tab%
Mary%A_Tab%Jones%A_Tab%Teacher%A_Tab%Her favorite song is "Driver".
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%
)

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


; search "Last name" column for containing "man" string
oFound := oTable.SearchColumn("Last name", "man")
;MsgBox % oFound.1.3
MsgBox % Obj2String(oFound)


; search "Occupation" and "Notes" columns for containing "Driver" or "artist" strings. "|" is query delimiter.
oFound2 := oTable.SearchColumn("Occupation|Notes", "Driver|artist")
MsgBox % Obj2String(oFound2)



;===Helper function======
Obj2String(obj, ColumnsDelimiter="`t", RowsDelimiter= "`n") {
   For k,v in obj
   {
      For k2,v2 in obj[k]
      FoundRow .= v2 ColumnsDelimiter
      FoundRow := Trim(FoundRow, ColumnsDelimiter)
      Found .= FoundRow RowsDelimiter
      FoundRow =
   }
   return Trim(Found, RowsDelimiter)
}

;===oTable Functions====================================================================
Table_ObjCreate(InputVariable, ColumnsDelimiter="`t", RowsDelimiter= "`n") {   ; not finished
   
   static base := Table_Base("SearchColumn", "Table_m")
   oTable := Object("base", base)
   oRows := Object()
   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)
         continue
      }
      RowNum := A_index-1
      %RowNum% := Object()
      Loop, parse, CurRow, %ColumnsDelimiter%
      %RowNum%.Insert(A_LoopField)
      oRows.Insert(%RowNum%)
   }
   
   oTable.Rows := oRows, oTable.ColumnNames := oColumnNames
   return oTable
}

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

Table_mSearchColumn(oTable, ColumnsToSearch, StringToSearch) {   ; not finished
   ; implemented "if field contains" for now. To add: exact match, regEx match, multiple filters.
   oFound := Object()
   ColumnsToSearch := Table_ColumnNames2Num(oTable, ColumnsToSearch)
   StringReplace, StringToSearch, StringToSearch, `,, `,`,, all
   StringReplace, StringToSearch, StringToSearch, |, `,, all
   For k in oTable.Rows
   {
      Loop, parse, ColumnsToSearch, |
      {
         CurField := oTable.Rows[k][A_LoopField]
         if CurField contains %StringToSearch%
         {
            %k% := Object()   
            For   k2,v2 in oTable.Rows[k]
            %k%.Insert(v2)
            oFound.Insert(%k%)
         }
      }
   }
   return oFound   ; I'm returning search results as a object - is that a overkill? - not in AHK simplicity spirit...
}

Table_ColumnNames2Num(oTable, ColumnsToSearch) {
   StringReplace, ColumnsToSearch, ColumnsToSearch, `,, `,`,, all
   StringReplace, ColumnsToSearch, ColumnsToSearch, |, `,, all
   For k,v in oTable.ColumnNames
   {
      if v in %ColumnsToSearch%
      Found .= k "|"
   }
   return Trim(Found, "|")
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 16th, 2010, 1:45 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
It is much better now.

Quote:
I'm unsure about SearchColumn method. Should I return search results as:
- object - current solution in code below - sounds more "proper" but more complicated - not in AHK simplicity spirit
- string


Definitely object. You should add to all objects u created (table, row, search [i.e. filtered_table]) ToString() function which will :

- if casted on table or search object return back string used in constructor (not the same string ofc, but the most actual one, perhaps with some other changes [u could let user change delimiter for instance]).
- if casted on row return back only that row as string

So, for the end user its the same. To get the string he would need only to specify:
Code:
oFound := oTable.SearchColumn("Last name", "man").ToString()



Table and search objects need count property.

You could also think about more filtering options, for instance regex filtering of table, returning subset etc.. altho now with object oriented interface its easy to get :

Code:
  oFilteredTable := oTable.FromScheme() ; create empty table with the same number and names of cols and same delimiter
  loop, % oTable.Rows.Count
        if oTable.Rows[i,1] ~= "^[0-9]+$"
            oFilteredTable.Rows.Add(oTable.Rows[i])

 msgbox %  oFilteredTable.ToString()

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 18th, 2010, 10:04 pm 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Little progress, but new questions...
Code:
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%
Jessica%A_Tab%Hickman%A_Tab%Student%A_Tab%
Mary%A_Tab%Jones%A_Tab%Teacher%A_Tab%Her favorite song is "Driver".
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%
)


oTable := Table_ObjCreate(Variable)   ; create table object
;MsgBox % oTable.Rows.3.2   ; get value from [3. row, 2. column]
;MsgBox % oTable.Rows.1.1 := "Bobby"   ; set [1. row, 1. column] to "Bobby"

MsgBox % String1 := oTable.ToString()   ; convert whole table back to string.


; search "Last name" column for containing "man" string
MsgBox % String2 := oTable.SearchColumn("Last name", "man").ToString()   ;  convert search results to string.
;oFound := oTable.SearchColumn("Last name", "man")   ; store search results as object
;MsgBox % oFound.1.3   ; get [1. row, 3. column] in search results


; search "Occupation" and "Notes" columns for containing "Driver" or "artist" strings. "|" is query delimiter.
oFound2 := oTable.SearchColumn("Occupation|Notes", "Driver|artist")      ; store search results as object




;===oTable Functions====================================================================
Table_ObjCreate(InputVariable, ColumnsDelimiter="`t", RowsDelimiter= "`n") {   ; not finished
   
   static base := Table_Base("SearchColumn ToString", "Table_m")
   oTable := Object("base", base, "ColumnsDelimiter", ColumnsDelimiter, "RowsDelimiter", RowsDelimiter)
   oRows := Object()
   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)
         continue
      }
      RowNum := A_index-1
      %RowNum% := Object()
      Loop, parse, CurRow, %ColumnsDelimiter%
      %RowNum%.Insert(A_LoopField)
      oRows.Insert(%RowNum%)
   }
   
   oTable.Rows := oRows, oTable.ColumnNames := oColumnNames
   return oTable
}

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

Table_mToString(oTable, ColumnsDelimiter="", RowsDelimiter= "") {
   if ColumnsDelimiter =
   ColumnsDelimiter := oTable.ColumnsDelimiter
   if RowsDelimiter =
   RowsDelimiter := oTable.RowsDelimiter
   
   For k,v in oTable.Rows
   {
      For k2,v2 in oTable.Rows[k]
      FoundRow .= v2 ColumnsDelimiter
      FoundRow := RTrim(FoundRow, ColumnsDelimiter)
      Found .= FoundRow RowsDelimiter
      FoundRow =
   }
   return RTrim(Found, RowsDelimiter)
}

Table_mSearchColumn(oTable, ColumnsToSearch, StringToSearch) {   ; not finished
   ; implemented "if field contains" for now. To add: exact match, regEx match, multiple filters.
   static base := Table_Base("ToString", "Table_mFound")
   
   oFound := Object("base", base)
   ColumnsToSearch := Table_ColumnNames2Num(oTable, ColumnsToSearch)
   StringReplace, StringToSearch, StringToSearch, `,, `,`,, all
   StringReplace, StringToSearch, StringToSearch, |, `,, all
   For k in oTable.Rows
   {
      Loop, parse, ColumnsToSearch, |
      {
         CurField := oTable.Rows[k][A_LoopField]
         if CurField contains %StringToSearch%
         {
            %k% := Object()      ; example: 1 := Object()
            For   k2,v2 in oTable.Rows[k]
            %k%.Insert(v2)
            oFound.Insert(%k%)
         }
      }
   }
   return oFound
}

Table_mFoundToString(oFound, ColumnsDelimiter="", RowsDelimiter= "") {   ; not finished
   if ColumnsDelimiter =
   ColumnsDelimiter := "`t"   ; bad improvisation!
   if RowsDelimiter =
   RowsDelimiter := "`n"   ; bad improvisation!
   
   For k,v in oFound
   {
      For k2,v2 in oFound[k]
      FoundRow .= v2 ColumnsDelimiter
      FoundRow := RTrim(FoundRow, ColumnsDelimiter)
      Found .= FoundRow RowsDelimiter
      FoundRow =
   }
   return RTrim(Found, RowsDelimiter)
}

Table_ColumnNames2Num(oTable, ColumnsToSearch) {
   StringReplace, ColumnsToSearch, ColumnsToSearch, `,, `,`,, all
   StringReplace, ColumnsToSearch, ColumnsToSearch, |, `,, all
   For k,v in oTable.ColumnNames
   {
      if v in %ColumnsToSearch%
      Found .= k "|"
   }
   return RTrim(Found, "|")
}

I want that ColumnsDelimiter and RowsDelimiter are optional in each ToString method. If not specified in method call, delimiters from constructor will be used.

Case 1: oTable.ToString()
no problems here - delimiters are optional. If not specified, oTable.ColumnsDelimiter and oTable.RowsDelimiter apply.
so I can call
Code:
MsgBox % oTable.ToString()   ; delimiters from constructor used
; or
MsgBox % oTable.ToString("|","#")   ; custom delimiters used


Case 2: oFound.ToString()
How can I access to oTable.ColumnsDelimiter and oTable.RowsDelimiter which are used in constructor when I'm inside oFound's ToString method?
Where to store those delimiters so I can access them? Maybe in storage function, so all objects will have access to delimiters? (like in radial menu codes) But than, they are not real properties... I also think it's wrong to create oFound.ColumnsDelimiter and oFound.RowsDelimiter keys, because oFonud elements should be just found rows IMO...
Code:
Table_Reg(variable, value="") {    ; Register (storage)
   static
   if (value = "") {   ; return variable's value
      yaqxswcdevfr := %variable%
      Return yaqxswcdevfr
   }
   Else         ; set value to variable
   %variable% = %value%
}
/*   ;Examples
Table_Reg("ColumnsDelimiter", ColumnsDelimiter)   ; store
ColumnsDelimiter := Table_Reg("ColumnsDelimiter")   ; get
*/
See "bad improvisation!" notes in first code block in this post.

* * *

Quote:
Table and search objects need count property;
oTable.Rows.Count
why not just:
Code:
oTable.Rows.MaxIndex()


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 18th, 2010, 11:42 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Why don't u just set it up when you create the object ?

in:
oFound := Object("base", base)

use
oFound := Object("base", base, "table", oTable)

Then you can access anything from search object as it keeps reference to its 'parent':

oFound.oTable.RowDelimiter.

Anyway, since oFound is another table, I suggest you first implementing Table.Clone as described above (this will automatically propagate delimiters and column names in fresh empty table, something that might be needed by users anyway) then create object using it.. You might for instance want to search results again .

so
Code:
Table_SearchColumn( oTable, ...) {
  oFound := oTable.Clone()
  oFound.table := oTable  ;could be set by Clone()
  ;populate oFound
  return oFound
}
oFound := oTable.ColumnSearch(...)
oFound.table --> oTable
oFound2 := oFound.ColumnSearch(....)
oFound2.table --> oFound   ;maybe some other name is better, like parent or parentTable....
oFound2.table.table -> oTable
oFound.toString() ; oFound is cloned so it has the same delimiter as its parent. Strictly speaking you don't need 'table' property then but it might be good to have it anyway. If oFound is not cloned then u need it, or at least you need its delimiter. Then again, if you clone Table for Found object, and user changes Table.RowDelimiter, delimiter in Found object will not follow, unless, again, you saved 'parent'. So its best to do so, and in toString() use oFound.table.RowDelimiter as separator



Quote:
why not just:
Code:
oTable.Rows.MaxIndex()

Yup, you could use that, the minor side is you don't have control over it.
If implementation change not to use integer indices or something it will not produce expected results. For instance, in large tables you might deduce that you don't want to delete rows (as it requires to move all above rows which is performance problem) but to mark them as deleted. If that happens, MaxIndex() will no longer represent valid row count.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 19th, 2010, 6:34 pm 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Good advices. I'm working on it.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 20th, 2010, 2:10 pm 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Latest work - new construction, methods...
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%
Jessica%A_Tab%Hickman%A_Tab%Student%A_Tab%
Mary%A_Tab%Jones%A_Tab%Teacher%A_Tab%Her favorite song is "Driver".
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%
)


oTable := Table_ObjCreate(Variable)   ; create table object

;MsgBox % oTable.3.2   ; get value from [3. row, 2. column]
;oTable.1.1 := "Bobby"   ; set [1. row, 1. column] to "Bobby"
oTable.AddRow("Joe", "Newman", "Kiteboarder", "Freestyle & Wave", "Do not store not existing column!")   ; add row
;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)


;=== 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)





;===oTable Functions====================================================================
Table_ObjCreate(InputVariable, ColumnsDelimiter="`t", RowsDelimiter= "`n") {   ; not finished
   
   static TableBase := Table_Base("SearchColumn ToString NewFromScheme AddRow", "Table_mTable_")
   static RowBase := Table_Base("ToString", "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) {
   oNewTable := Object("base", oTable.base, "ColumnsDelimiter", oTable.ColumnsDelimiter
      , "RowsDelimiter", oTable.RowsDelimiter, "ColumnNames", oTable.ColumnNames)
   return oNewTable
}

Table_mTable_ToString(oTable, ColumnsDelimiter="", RowsDelimiter= "") {
   if ColumnsDelimiter =
   ColumnsDelimiter := oTable.ColumnsDelimiter
   if RowsDelimiter =
   RowsDelimiter := oTable.RowsDelimiter

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

Table_mTable_SearchColumn(oTable, ColumnsToSearch, StringToSearch) {   ; not finished
   ; implemented "if field contains" for now. To add: exact match, regEx match
   static RowBase := Table_Base("ToString", "Table_mRow_")
   
   oFound := oTable.NewFromScheme()   ; create empty table from oTable template
   
   ColumnsToSearch := Table_ColumnNames2Num(oTable, ColumnsToSearch)
   StringReplace, StringToSearch, StringToSearch, `,, `,`,, all
   StringReplace, StringToSearch, StringToSearch, |, `,, all
   For k in oTable
   {
      if k is not integer   ;Rows are integers. Keys to skip: ColumnsDelimiter, RowsDelimiter, ColumnNames
      continue
      Loop, parse, ColumnsToSearch, |
      {
         CurField := oTable[k][A_LoopField]
         if CurField contains %StringToSearch%
         {
            %k% := Object("base", RowBase, "table", oTable)
            For   k2,v2 in oTable[k]
            %k%.Insert(v2)
            oFound.Insert(%k%)
         }
      }
   }
   return oFound
}

Table_mTable_AddRow(oTable,Fields*) {
   static RowBase := Table_Base("ToString", "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%)
}

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

;====== Shared, other ======
Table_ColumnNames2Num(oTable, ColumnsToSearch) {
   StringReplace, ColumnsToSearch, ColumnsToSearch, `,, `,`,, all
   StringReplace, ColumnsToSearch, ColumnsToSearch, |, `,, all
   For k,v in oTable.ColumnNames
   {
      if v in %ColumnsToSearch%
      Found .= k "|"
   }
   return RTrim(Found, "|")
}


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 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: notsoobvious, rrhuffy, Yahoo [Bot] and 11 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