[Implemented] Dictionary

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
USCode
Addict
Addict
Posts: 923
Joined: Wed Mar 24, 2004 11:04 pm
Location: Seattle

[Implemented] Dictionary

Post by USCode »

Has anyone ever requested a Dictionary collection library for PureBasic?
This is a very handy construct that maps keys to values and is available in many languages such as Java, RealBasic, Python, even C++:
http://en.wikipedia.org/wiki/Associative_array
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Well! If anyone feel up to coding a ASM implementation or C implementation maybe Fred can add that later :P

Very good suggestion though, for the time being I'm doing something similar manually. Bit kind of brute force. (iterating through all keys until correct one is found)
Not the most effective way for sure!

EDIT: hash trees are usually the fastest implementation unless I remember wrong! But not that easy to code!
akj
Enthusiast
Enthusiast
Posts: 668
Joined: Mon Jun 09, 2003 10:08 pm
Location: Nottingham

Post by akj »

Here is my attempt at implementing a dictionary (associative array):

Code: Select all

; AArray  AKJ  12-Apr-06
; Associative Array or Dictionary
; An 'array' in which you lookup a value by it's name rather than by an index number
; In this implementation, names and values are both case-sensitive strings.

; The method used is crude & probably slow, but may serve as a benchmark for other methods

EnableExplicit
Global AArray$ ; Holds name/value pairs:  Chr(1)+<Name>+Chr(2)+<Value>+Chr(3)

Procedure.l AddKey(name$, value$)
; Create a name/value pair or, if the name already exists, associate it with a new value
; Return the number of keys added (i.e. 1, or 0 if the name already exists)
Protected p, q
name$=Chr(1)+Trim(name$)+Chr(2): value$=Trim(value$)
p=FindString(AArray$, name$, 1) ; Point to Chr(1)
If p
  p+Len(name$)-1 ; Point to Chr(2)
  q=FindString(AArray$, Chr(3), p+1) ; Point to Chr(3)
  AArray$=Left(AArray$, p)+value$+Mid(AArray$, q, 99999999)
  ProcedureReturn 0
EndIf
AArray$+name$+value$+Chr(3)
ProcedureReturn 1
EndProcedure

Procedure.l DelName(name$)
; Delete the name/value pair with the given name
; Return the number of keys deleted (0 or 1)
Protected p, q
name$=Chr(1)+Trim(name$)+Chr(2)
p=FindString(AArray$, name$, 1) ; Point to Chr(1)
If p
  q=FindString(AArray$, Chr(3), p+Len(name$)) ; Point to Chr(3)
  AArray$=Left(AArray$, p-1)+Mid(AArray$, q+1, 99999999) ; Delete
  ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure

Procedure.l DelValue(value$)
; Delete all name/value pairs with the given value
; Return the number of keys deleted (>=0)
Protected p, q, r=0
value$=Chr(2)+Trim(value$)+Chr(3)
p=FindString(AArray$, value$, 1) ; Point to Chr(2)
While p
  q=p+Len(value$) ; Point to byte after Chr(3)
  Repeat: p-1: Until Mid(AArray$, p, 1)=Chr(1) ; Point to preceding Chr(1)
  AArray$=Left(AArray$, p-1)+Mid(AArray$, q, 99999999) ; Delete
  p=FindString(AArray$, value$, p+1) ; Point to Chr(2)
  r+1
Wend
ProcedureReturn r
EndProcedure

Procedure.l CountKeys()
; Return the number of name/value pairs in the associative array
ProcedureReturn CountString(AArray$, Chr(2))
EndProcedure

Procedure.l CountNames(name$)
; Return the number of name/value pairs with the given name
ProcedureReturn CountString(AArray$, Chr(1)+Trim(name$)+Chr(2))
EndProcedure

Procedure.l CountValues(value$)
; Return the number of name/value pairs with the given value
ProcedureReturn CountString(AArray$, Chr(2)+Trim(value$)+Chr(3))
EndProcedure

Procedure.s FindName(name$)
; Return the value associated with the given name, or "" on failure
Protected p, q
name$=Chr(1)+Trim(name$)+Chr(2)
p=FindString(AArray$, name$, 1) ; Point to Chr(1)
If p
  p+Len(name$) ; Point to byte after Chr(2)
  q=FindString(AArray$, Chr(3), p) ; Point to Chr(3)
  ProcedureReturn Mid(AArray$, p, q-p)
EndIf
ProcedureReturn ""
EndProcedure

Procedure.s FindValue(value$, sep$=" ")
; Return all names associated with the given value, else "" on failure
; The names are separated by sep$ (one or more characters)
Protected p, q, r$=""
value$=Chr(2)+Trim(value$)+Chr(3)
q=FindString(AArray$, value$, 1) ; Point to Chr(2)
While q
  p=q
  Repeat: p-1: Until Mid(AArray$, p, 1)=Chr(1) ; Point to preceding Chr(1)
  r$+sep$+Mid(AArray$, p+1, q-p-1)
  q=FindString(AArray$, value$, q+Len(value$)) ; Point to Chr(2)
Wend
ProcedureReturn Mid(r$, Len(sep$)+1, 99999999) ; Omit the leading separator
EndProcedure

Procedure.s SortNames(sep$=" ")
; Sort the associative array keys in name order.  Also ...
; Return all names in sorted order, separated by sep$ (one or more characters)
Protected NewList names$(), p, q, r$
If Len(AArray$)=0: ProcedureReturn "": EndIf
p=1 ; Point to Chr(1)
Repeat ; Extract AArray$ contents to a linked list
  q=FindString(AArray$, Chr(3), p+1) ; Point to Chr(3)
  AddElement(names$()): names$()=Mid(AArray$, p+1, q-p) ; Exclude Chr(1), include Chr(3)
  p=q+1 ; Point to Chr(1)
Until p>=Len(AArray$)
SortList(names$(), 0) ; Sort the linked list
AArray$="": r$=""
ForEach names$() ; Rebuild AArray$ from the linked list
  AArray$+Chr(1)+names$()
  r$+sep$+Left(names$(), FindString(names$(), Chr(2),1)-1)
Next names$()
ProcedureReturn Mid(r$, Len(sep$)+1, 99999999) ; Omit the leading separator
EndProcedure

; Test program (value is the number of chars in the name)
Debug "Count 0 = "+Str(CountKeys())
Debug "Add key = "+Str(AddKey("melon","5"))
Debug "Add key = "+Str(AddKey("apple","9")) ; Wrong value of 9; corrected later
Debug "Add key = "+Str(AddKey("orange","6"))
Debug "Add key = "+Str(AddKey("pear","4"))
Debug "Add dup = "+Str(AddKey("apple","5")) ; Corrected value
Debug "Add key = "+Str(AddKey("banana","6"))
Debug "Add dup = "+Str(AddKey("orange","6"))
Debug "Add key = "+Str(AddKey("raspberry","9"))
Debug "Add key = "+Str(AddKey("grape","5"))
Debug "Count 7 = "+Str(CountKeys())
Debug "?N apple= "+FindName("apple")
Debug "?N raspb= "+FindName("raspberry")
Debug "?V 5    = "+FindValue("5", "+")
Debug "?V 6    = "+FindValue("6")
Debug "-N pear = "+Str(DelName("pear"))
Debug "-N pear = "+Str(DelName("pear")) ; Already deleted
Debug "-V 6    = "+Str(DelValue("6")) ; Delete all keys with value "6"
Debug "?V 6    = "+FindValue("6") ; Nothing to find
Debug "Add key = "+Str(AddKey("peach","5"))
Debug "Sort N  = ["+SortNames("]  [")+"]"
Debug "Count 5 = "+Str(CountKeys())
End
Anthony Jordan
USCode
Addict
Addict
Posts: 923
Joined: Wed Mar 24, 2004 11:04 pm
Location: Seattle

Post by USCode »

akj wrote:Here is my attempt at implementing a dictionary (associative array):
Thanks akj, it's a start! :D
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

this is my attemps to implement dictionary.
feel free to update it...

Code: Select all

; Object:  Dictionary (Associative Array)
; Version: 0.1
; Author:  flype
; Requir.  Purebasic 4.0+

EnableExplicit

; AssociativeArray.pbi

Structure DICTIONARY
  key.s
  value.s
EndStructure

Macro dict_dim(dict)
  NewList dict.DICTIONARY()
EndMacro

Procedure.l dict_is(dict.DICTIONARY(), key.s = "")
  If key
    ForEach dict()
      If dict()\key = key
        ProcedureReturn #True
      EndIf
    Next
  Else
    If CountList(dict())
      ProcedureReturn #True
    EndIf
  EndIf
EndProcedure
Procedure.l dict_del(dict.DICTIONARY(), key.s = "")
  If key
    ForEach dict()
      If dict()\key = key
        DeleteElement(dict())
        ProcedureReturn #True
      EndIf
    Next
  Else
    ClearList(dict())
  EndIf
EndProcedure
Procedure.l dict_new(dict.DICTIONARY(), key.s, Value.s = "")
  If key
    ForEach dict()
      If dict()\key = key
        ProcedureReturn #False
      EndIf
    Next
    If AddElement(dict())
      dict()\key = key
      dict()\Value = Value
      ProcedureReturn #True
    EndIf
  EndIf
EndProcedure
Procedure.l dict_set(dict.DICTIONARY(), key.s, Value.s = "")
  ForEach dict()
    If dict()\key = key
      dict()\Value = Value
      ProcedureReturn #True
    EndIf
  Next
EndProcedure
Procedure.l dict_index(dict.DICTIONARY(), key.s)
  Protected value.s
  ForEach dict()
    If dict()\key = key
      ProcedureReturn ListIndex(dict())
    EndIf
  Next
EndProcedure
Procedure.s dict_value(dict.DICTIONARY(), key.s = "")
  Protected value.s
  ForEach dict()
    If key = "" Or dict()\key = key
      value + dict()\value + ", "
    EndIf
  Next
  ProcedureReturn value
EndProcedure
Procedure.l dict_values(dict.DICTIONARY(), values.s(), key.s = "")
  ForEach dict()
    If key = "" Or dict()\key = key
      If AddElement(values())
        values() = dict()\value
      EndIf
    EndIf
  Next
EndProcedure
Procedure.s dict_key(dict.DICTIONARY(), value.s = "")
  Protected key.s
  ForEach dict()
    If value = "" Or dict()\value = value
      key + dict()\key + ", "
    EndIf
  Next
  ProcedureReturn key
EndProcedure
Procedure.l dict_keys(dict.DICTIONARY(), keys.s(), value.s = "")
  ForEach dict()
    If value = "" Or dict()\value = value
      If AddElement(keys())
        keys() = dict()\key
      EndIf
    EndIf
  Next
EndProcedure
Procedure.l dict_count(dict.DICTIONARY(), value.s = "")
  Protected result.l
  If value
    ForEach dict()
      If dict()\value = value
        result + 1
      EndIf
    Next
  Else
    result = CountList(dict())
  EndIf
  ProcedureReturn result
EndProcedure
Procedure.l dict_sort(dict.DICTIONARY(), options.l, member.l, first.l = 0, last.l = 0)
  If last = 0
    last = CountList(dict())
  ElseIf last < 0
    last = CountList(dict()) + last
  EndIf
  Select member
    Case 0: SortStructuredList(dict(), options, OffsetOf(DICTIONARY\key), #PB_Sort_String, first, last)
    Case 1: SortStructuredList(dict(), options, OffsetOf(DICTIONARY\value), #PB_Sort_String, first, last)
  EndSelect
EndProcedure
Procedure.s dict_implode(dict.DICTIONARY(), op.s, sep.s)
  Protected result.s
  ForEach dict()
    result + dict()\key + op
    result + dict()\value + sep
  Next
  ProcedureReturn result
EndProcedure
Procedure.l dict_explode(dict.DICTIONARY(), string.s, op.s, sep.s)
  Protected i.l, field.s, n = CountString(string, sep) + 1
  For i = 1 To n
    field = StringField(string, i, sep)
    dict_new(dict(), Trim(StringField(field, 1, op)), StringField(field, 2, op))
  Next
  ProcedureReturn CountList(dict())
EndProcedure

; AssociativeArray_TEST.pb

Procedure Main(test.s)
  
  dict_dim(a)
  
  Debug dict_explode(a(), test, "=", " ")
  
  Debug dict_count(a())
  Debug dict_count(a(), "yes")
  
  Debug dict_index(a(), "revision")
  
  Debug dict_key(a(), "11")
  Debug dict_value(a(), "os")
  
  Debug dict_key(a())
  Debug dict_value(a())
  
  Debug dict_implode(a(), "-->", "  ///  ")
  
  dict_sort(a(), 0, 0)
  
  Debug dict_implode(a(), "-->", "  ///  ")
  
  dict_sort(a(), 0, 1)
  
  Debug dict_implode(a(), "-->", "  ///  ")
  
EndProcedure

Main("name=purebasic version=4.0 revision=11 os=windows registered=yes uptodate=yes")
Last edited by Flype on Sun Apr 30, 2006 8:45 am, edited 1 time in total.
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
USCode
Addict
Addict
Posts: 923
Joined: Wed Mar 24, 2004 11:04 pm
Location: Seattle

Post by USCode »

Thanks Flype. Unfortunately, I can't get your code to compile with PB4B11, PBCompiler.exe keeps abending out ... anyone else experiencing this problem?
If so then I'll post a bug report with Fred...
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

did you downloaded from www.purebasic.com/beta/ the files provided by fred ? maybe... because it works well for me :roll:
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
USCode
Addict
Addict
Posts: 923
Joined: Wed Mar 24, 2004 11:04 pm
Location: Seattle

Post by USCode »

:roll:
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

uscode, where (lines) does it fail ?
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

Fred has said that its on his todo list:

http://www.purebasic.fr/english/viewtopic.php?t=17664
USCode
Addict
Addict
Posts: 923
Joined: Wed Mar 24, 2004 11:04 pm
Location: Seattle

Post by USCode »

Flype wrote:uscode, where (lines) does it fail ?
After I hit Compile/Run, this is all I get back, no error lines, etc. As I said before, the PB Compiler itself is abending:
Image
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

hi, sorry to bumping this thread but does my 'associative array' include still crash for some of us ?

i've tested with the lastest pb, it's ok (it never crashed at home for me).
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
Dare
Addict
Addict
Posts: 1965
Joined: Mon May 29, 2006 1:01 am
Location: Outback

Post by Dare »

Hi Flype

Doesn't crash here. Win XP Pro
Dare2 cut down to size
User avatar
bembulak
Enthusiast
Enthusiast
Posts: 575
Joined: Mon Mar 06, 2006 3:53 pm
Location: Austria

Post by bembulak »

Works like a charm on Win XP Pro PB 4.10 beta 2 and Ubuntu Gnu/Linux Feisty Fawn 7.04 PB 4.02.
cheers,

bembulak
Post Reply