keyed value types with pseudo-dict - Methods 
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © kaigouthro
//@version=5

//@description a set of primitive single-type key/value items, and pseudo-dictionaries
// The dictionaries are single arrays of items, each item has a key and value.
// The library is 100% methods. add, remove, get and set items by key. 
// It provides methods to get all the keys, values or items in the dict as arrays.
// For those familiar with python, it's similar, but typified.. and the brackets are round.
library("key_vals")

///////  description-ish, not all comments are filled, my keyboard
// is nearly at it's end, missing  keys, and another dozen
// don't quite listen. but code works.. probably.. maybe... 



//     example syntax.. using floats.. i should do this more often.
//     ... the same functions exist for Int, Bool and String

//------------------------------------------
//     new float_item
//
//     new ('demo', 1.0 )


//------------------------------------------
//     set the key of a float_item
//
//     float_item.key ('demo')


//------------------------------------------
//     get the key of a float_item
//
//     float_item.key ()


//------------------------------------------
//     set the value of a float_item
//
//     float_item.val ('demo')


//------------------------------------------
//     get the value of a float_item
//
//     float_item.val ()


//------------------------------------------
//     get the number of items in a dict
//
//     float_dict.count ()


//------------------------------------------
//     set an item to a dict by key ( add / update)
//
//     float_dict.set   ( 'demo', 2.0 )


//------------------------------------------
//     remove an item from a dict by key (changes index, but this is about keys, not ids)
//
//     float_dict.remove   ( 'demo' )


//------------------------------------------
//     get an item from a dict array
//
//     array<float_dict>.get   ( 0 )


//------------------------------------------
//     get an item from a dict by key
//
//     float_dict.get   ( 'demo' )

//------------------------------------------
//     get an item from a dict by id (remember, ids change if removed by key)
//     mainlyy an internal function
//
//     float_dict.get   ( 0 )


//------------------------------------------
//     return all keys as string[]
//
//     float_dict.get_keys()


//------------------------------------------
//     return all values as float[]
//
//     float_dict.get_values()


//------------------------------------------
//     return all items as float[]
//
//     float_dict.get_items()


//------------------------------------------
//     create a new float dict in the multi dict and return the index of it
//
//     multi_dict.add_float_dict()


//------------------------------------------
//     get the float dict at index 0 from the multi dict
//
//     multi_dict.get_float_dict( 0)


//------------------------------------------
//     set an item in a float dict in a multi dict
//
//     multi_dict.set_float( 'demo', 2.0 )


//------------------------------------------
//     get an item from a float dict in a multi dict
//
//     multi_dict.get_float( 'demo' )



// @type  float_item
// @field key (string) 
// @field val (float) 
// @field next (float_item) 
export type float_item
    string       key  = ''
    float        val  = 0.
    float_item   next


// @type  float_dict
export type float_dict
    float_item[] content


// @type  int_item
// @field key (string) 
// @field val (int) 
// @field next (int_item) 
export type int_item
    string       key  = ''
    int          val  = 0
    int_item     next


// @type  int_dict
export type int_dict
    int_item[] content


// @type  string_item
// @field key (string) 
// @field val (string) 
// @field next (string_item) 
export type string_item
    string       key  = ''
    string       val  = ''
    string_item  next


// @type  string_dict
export type string_dict
    string_item[] content


// @type  bool_item
// @field key (string) 
// @field val (bool) 
// @field next (bool_item) 
export type bool_item
    string       key  = ''
    bool         val  = na
    bool_item    next


// @type  bool_dict
export type bool_dict
    bool_item[] content



// @type  multi_dict
export type multi_dict
    float_dict []  f_dicts
    int_dict     f_registry
    int_dict   []  i_dicts
    int_dict     i_registry
    string_dict[]  s_dicts
    int_dict     s_registry
    bool_dict  []  b_dicts
    int_dict     b_registry



// @function new
// @param 	 key	string
// @param 	 val	float
// @returns
export new          (  string       key  ,  float    val   ) => float_item.new (key,val)

// @function key
// @param 	 this	float_item
// @param 	 key	string
// @returns
export method key   (  float_item   this ,  string   key   ) => this.key  := key , this

// @function key
// @param 	 this	float_item
// @returns
export method key   (  float_item   this                   ) => this.key 

// @function val
// @param 	 this	float_item
// @returns
export method val   (  float_item   this                   ) => this.val

// @function val
// @param 	 this	float_item
// @param 	 val	float
// @returns
export method val   (  float_item   this ,  float    val   ) => this.val  := val , this

// @function count
// @param 	 dict	float_dict
// @returns
export method count (  float_dict   dict                   ) => array.size (dict.content)

// @function get
// @param 	 dict	float_dict
// @param 	 index	int
// @returns
export method get   (  float_dict   dict ,  int      index ) => (index >= 0 and index < array.size(dict.content)) ? array.get(dict.content , index ) : na

// @function get
// @param 	 dict	float_dict
// @param 	 index	int
// @returns
export method get   (  float_dict[]  dict ,  int      index ) => (index >= 0 and index < array.size(dict)) ? array.get(dict , index ) : na


// @function set
// @param 	 d	float_dict
// @param 	 key	string
// @param 	 val	float
// @returns
export method set(float_dict d, string key, float val) => 
    int index = -1
    for i     = 0 to array.size(d.content) - 1
        if d.get(i).key == key
            v = d.get(i).val
            v := val
            break
            index := i
    if index < 0  
        array.push(d.content, float_item.new(key,val))


// @function get
// @param 	 d	float_dict
// @param 	 key	string
// @returns
export method get(float_dict d, string key) => 
    float out = na
    for   i   = 0 to array.size(d.content) - 1
        if d.get(i).key == key
            out := d.get(i).val
    out


// @function get_keys
// @param 	 d	float_dict
// @returns
export method get_keys(float_dict d) => 
    string[] keys = array.new_string()
    for    i      = 0 to array.size(d.content) - 1
        array.push(keys, d.get(i).key)
    keys


// @function get_values
// @param 	 d	float_dict
// @returns
export method get_values(float_dict d) => 
    float[] values = array.new_float()
    for   i        = 0 to array.size(d.content) - 1
        array.push(values, d.get(i).val)
    values


// @function get_items
// @param 	 d	float_dict
// @returns
export method get_items(float_dict d) => 
    float_item[] content = array.new<float_item>()
    for        i         = 0 to array.size(d.content) - 1
        array.push(content, d.get(i))
    content



// @function new
// @param 	 key	string
// @param 	 val	bool
// @returns
export new          (  string       key  ,  bool    val    ) => bool_item.new (key,val)

// @function key
// @param 	 this	bool_item
// @param 	 key	string
// @returns
export method key   (  bool_item    this ,  string   key   ) => this.key  := key , this

// @function key
// @param 	 this	bool_item
// @returns
export method key   (  bool_item    this                   ) => this.key 

// @function val
// @param 	 this	bool_item
// @returns
export method val   (  bool_item    this                   ) => this.val

// @function val
// @param 	 this	bool_item
// @param 	 val	bool
// @returns
export method val   (  bool_item    this ,  bool    val    ) => this.val  := val , this

// @function count
// @param 	 dict	bool_dict
// @returns
export method count (  bool_dict    dict                   ) => array.size (dict.content)

// @function get
// @param 	 dict	bool_dict
// @param 	 index	int
// @returns
export method get   (  bool_dict    dict ,  int      index ) => (index >= 0 and index < array.size(dict.content)) ? array.get(dict.content , index ) : na

// @function get
// @param 	 dict	bool_dict
// @param 	 index	int
// @returns
export method get   (  bool_dict[]  dict ,  int      index ) => (index >= 0 and index < array.size(dict)) ? array.get(dict , index ) : na

// @function set
// @param 	 d	bool_dict
// @param 	 key	string
// @param 	 val	bool
// @returns
export method set(bool_dict d, string key, bool val) => 
    int index = -1
    for i     = 0 to array.size(d.content) - 1
        if d.get(i).key == key
            v = d.get(i).val
            v := val
            break
            index := i
    if index < 0  
        array.push(d.content, bool_item.new(key,val))


// @function get
// @param 	 d	bool_dict
// @param 	 key	string
// @returns
export method get(bool_dict d, string key) => 
    bool out = na
    for   i   = 0 to array.size(d.content) - 1
        if d.get(i).key == key
            out := d.get(i).val
    out


// @function get_keys
// @param 	 d	bool_dict
// @returns
export method get_keys(bool_dict d) => 
    string[] keys = array.new_string()
    for    i      = 0 to array.size(d.content) - 1
        array.push(keys, d.get(i).key)
    keys


// @function get_values
// @param 	 d	bool_dict
// @returns
export method get_values(bool_dict d) => 
    bool[] values = array.new_bool()
    for   i        = 0 to array.size(d.content) - 1
        array.push(values, d.get(i).val)
    values


// @function get_items
// @param 	 d	bool_dict
// @returns
export method get_items(bool_dict d) => 
    bool_item[] content = array.new<bool_item>()
    for        i         = 0 to array.size(d.content) - 1
        array.push(content, d.get(i))
    content



// @function new
// @param 	 key	string
// @param 	 val	int
// @returns
export new          (  string       key  ,  int      val   ) => int_item.new (key,val)

// @function key
// @param 	 this	int_item
// @param 	 key	string
// @returns
export method key   (  int_item     this ,  string   key   ) => this.key  := key , this

// @function key
// @param 	 this	int_item
// @returns
export method key   (  int_item     this                   ) => this.key 

// @function val
// @param 	 this	int_item
// @returns
export method val   (  int_item     this                   ) => this.val

// @function val
// @param 	 this	int_item
// @param 	 val	int
// @returns
export method val   (  int_item     this ,  int      val   ) => this.val  := val , this

// @function count
// @param 	 dict	int_dict
// @returns
export method count (  int_dict     dict                   ) => array.size (dict.content)

// @function get
// @param 	 dict	int_dict
// @param 	 index	int
// @returns
export method get   (  int_dict     dict ,  int      index ) => (index >= 0 and index < array.size(dict.content)) ? array.get(dict.content , index ) : na

// @function get
// @param 	 dict	int_dict
// @param 	 index	int
// @returns
export method get   (  int_dict[]  dict ,  int      index ) => (index >= 0 and index < array.size(dict)) ? array.get(dict , index ) : na

// @function set
// @param 	 d	int_dict
// @param 	 key	string
// @param 	 val	int
// @returns
export method set(int_dict d, string key, int val) => 
    int index = -1
    for i     = 0 to array.size(d.content) - 1
        if d.get(i).key == key
            v = d.get(i).val
            v := val
            break
            index := i
    if index < 0  
        array.push(d.content, int_item.new(key,val))


// @function get
// @param 	 d	int_dict
// @param 	 key	string
// @returns
export method get(int_dict d, string key) => 
    int out = na
    for   i   = 0 to array.size(d.content) - 1
        if d.get(i).key == key
            out := d.get(i).val
    out


// @function get_keys
// @param 	 d	int_dict
// @returns
export method get_keys(int_dict d) => 
    string[] keys = array.new_string()
    for    i      = 0 to array.size(d.content) - 1
        array.push(keys, d.get(i).key)
    keys


// @function get_values
// @param 	 d	int_dict
// @returns
export method get_values(int_dict d) => 
    int[] values = array.new_int()
    for   i        = 0 to array.size(d.content) - 1
        array.push(values, d.get(i).val)
    values


// @function get_items
// @param 	 d	int_dict
// @returns
export method get_items(int_dict d) => 
    int_item[] content = array.new<int_item>()
    for        i         = 0 to array.  size(d.content) - 1
        array.push(content, d.get(i))
    content



// @function new
// @param 	 key	string
// @param 	 val	string
// @returns
export new          (  string       key  ,  string   val   ) => string_item.new (key,val)

// @function key
// @param 	 this	string_item
// @param 	 key	string
// @returns
export method key   (  string_item  this ,  string   key   ) => this.key  := key , this

// @function key
// @param 	 this	string_item
// @returns
export method key   (  string_item  this                   ) => this.key 

// @function val
// @param 	 this	string_item
// @returns
export method val   (  string_item  this                   ) => this.val

// @function val
// @param 	 this	string_item
// @param 	 val	string
// @returns
export method val   (  string_item  this ,  string   val   ) => this.val  := val , this

// @function count
// @param 	 dict	string_dict
// @returns
export method count (  string_dict  dict                   ) => array.size (dict.content)

// @function get
// @param 	 dict	string_dict
// @param 	 index	int
// @returns
export method get   (  string_dict  dict ,  int      index ) => (index >= 0 and index < array.size(dict.content)) ? array.get(dict.content , index ) : na

// @function get
// @param 	 dict	string_dict
// @param 	 index	int
// @returns
export method get   (  string_dict[]  dict ,  int      index ) => (index >= 0 and index < array.size(dict)) ? array.get(dict , index ) : na

// @function set
// @param 	 d	string_dict
// @param 	 key	string
// @param 	 val	string
// @returns
export method set(string_dict d, string key, string val) => 
    int index = -1
    for i     = 0 to array.size(d.content) - 1
        if d.get(i).key == key
            v = d.get(i).val
            v := val
            break
            index := i
    if index < 0  
        array.push(d.content, string_item.new(key,val))


// @function get
// @param 	 d	string_dict
// @param 	 key	string
// @returns
export method get(string_dict d, string key) => 
    string out = na
    for   i   = 0 to array.size(d.content) - 1
        if d.get(i).key == key
            out := d.get(i).val
    out


// @function get_keys
// @param 	 d	string_dict
// @returns
export method get_keys(string_dict d) => 
    string[] keys = array.new_string()
    for    i      = 0 to array.size(d.content) - 1
        array.push(keys, d.get(i).key)
    keys


// @function get_values
// @param 	 d	string_dict
// @returns
export method get_values(string_dict d) => 
    string[] values = array.new_string()
    for   i        = 0 to array.size(d.content) - 1
        array.push(values, d.get(i).val)
    values


// @function get_items
// @param 	 d	string_dict
// @returns
export method get_items(string_dict d) => 
    string_item[] content = array.new<string_item>()
    for        i         = 0 to array.size(d.content) - 1
        array.push(content, d.get(i))
    content
    

// @function remove an item by key
// @param 	 key string
// @returns true if found and removed
export method remove(float_dict dict, string key)=>
    for [i,n] in dict.content
        if dict.get(i).key == key
            array.remove(dict.content, i)
            true
        else
            false

// @function remove an item by key
// @param 	 key string
// @returns true if found and removed
export method remove(string_dict dict, string key)=>
    for [i,n] in dict.content
        if dict.get(i).key == key
            array.remove(dict.content, i)
            true
        else
            false

// @function remove an item by key
// @param 	 key string
// @returns true if found and removed
export method remove(int_dict dict, string key)=>
    for [i,n] in dict.content
        if dict.get(i).key == key
            array.remove(dict.content, i)
            true
        else
            false

// @function remove an item by key
// @param 	 key string
// @returns true if found and removed
export method remove(bool_dict dict, string key)=>
    for [i,n] in dict.content
        if dict.get(i).key == key
            array.remove(dict.content, i)
            true
        else
            false




// @function Create a multi dict with some deep layers available (for the intrepid)
export create_multi_dict() => multi_dict.new()


// @function add_float_dict
// @param 	 this	multi_dict
// @returns
export method add_float_dict(multi_dict this) => 
    int index = math.max ( array.size(this.f_registry.content),0)
    fd = float_dict.new  ( )                                     
    array.push           ( this.f_dicts, fd)                     
    id = int_item.new    ( 'fd' + str.tostring(index), index)    
    array.push           ( this.f_registry.content, id)                  
    index                                                        


// @function add_int_dict
// @param 	 this	multi_dict
// @returns
export method add_int_dict(multi_dict this) => 
    int index = math.max ( array.size(this.i_registry.content),0)
    id = int_dict.new   ( )                                      
    array.push          ( this.i_dicts, id)                      
    ii = int_item.new   ( 'id' + str.tostring(index), index)     
    array.push          ( this.i_registry.content, ii)                   
    index                                                        

    
// @function add_string_dict
// @param 	 this	multi_dict
// @returns
export method add_string_dict(multi_dict this) => 
    int index = math.max ( array.size(this.s_registry.content),0)
    sd = string_dict.new   ( )                                      
    array.push             ( this.s_dicts, sd)                      
    si = int_item.new      ( 'sd' + str.tostring(index), index)     
    array.push             ( this.s_registry.content, si)                   
    index                                                           

    
// @function add_bool_dict
// @param 	 this	multi_dict
// @returns
export method add_bool_dict(multi_dict this) => 
    int index = math.max ( array.size(this.b_registry.content),0)
    bd = bool_dict.new   ( )                                      
    array.push           ( this.b_dicts, bd)                      
    bi = int_item.new    ( 'bd' + str.tostring(index), index)     
    array.push           ( this.b_registry.content, bi)                   
    index                                                        


// @function get_float_dict
// @param 	 this	multi_dict
// @param 	 index	int
// @returns
export method get_float_dict(multi_dict this, int index) => (index >= 0 and index < array.size(this.f_dicts)) ? array.get(this.f_dicts,index) : na  

    
// @function get_int_dict
// @param 	 this	multi_dict
// @param 	 index	int
// @returns
export method get_int_dict(multi_dict this, int index) => (index >= 0 and index < array.size(this.i_dicts)) ? array.get(this.i_dicts,index) : na  

    
// @function get_string_dict
// @param 	 this	multi_dict
// @param 	 index	int
// @returns
export method get_string_dict(multi_dict this, int index) => (index >= 0 and index < array.size(this.s_dicts)) ? array.get(this.s_dicts,index) : na  

// @function get_bool_dict
// @param 	 this	multi_dict
// @param 	 index	int
// @returns
export method get_bool_dict(multi_dict this, int index) => (index >= 0 and index < array.size(this.b_dicts)) ? array.get(this.b_dicts,index) : na  

// @function get_float_dict
// @param 	 this	multi_dict
// @param 	 key	string
// @returns
export method get_float_dict(multi_dict this, string key) => 
    int index = -1                                               
    for i     = 0 to array.size(this.f_registry.content) - 1             
        if this.f_registry.content.get(i).key == key                         
            index := this.f_registry.content.get(i).val                       
    (index >= 0 and index < array.size(this.f_dicts)) ? array.get(this.f_dicts,index) : na  
    
// @function get_int_dict
// @param 	 this	multi_dict
// @param 	 key	string
// @returns
export method get_int_dict(multi_dict this, string key) => 
    int index = -1                                               
    for i     = 0 to array.size(this.i_registry.content) - 1             
        if this.i_registry.content.get(i).key == key                         
            index := this.i_registry.content.get(i).val                      
    (index >= 0 and index < array.size(this.i_dicts)) ? array.get(this.i_dicts,index) : na  

// @function get_string_dict
// @param 	 this	multi_dict
// @param 	 key	string
// @returns
export method get_string_dict(multi_dict this, string key) => 
    int index = -1                                               
    for i     = 0 to array.size(this.s_registry.content) - 1             
        if this.s_registry.content.get(i).key == key                         
            index := this.s_registry.content.get(i).val                      
    (index >= 0 and index < array.size(this.s_dicts)) ? array.get(this.s_dicts,index) : na  
    
// @function get_bool_dict
// @param 	 this	multi_dict
// @param 	 key	string
// @returns
export method get_bool_dict(multi_dict this, string key) => 
    int index = -1                                               
    for i     = 0 to array.size(this.b_registry.content) - 1             
        if this.b_registry.content.get(i).key == key                         
            index := this.b_registry.content.get(i).val                      
    (index >= 0 and index < array.size(this.b_dicts)) ? array.get(this.b_dicts,index) : na  

// @function set_float
// @param 	 this	multi_dict
// @param 	 key	string
// @param 	 val	float
// @returns
export method set_float(multi_dict this, string key, float val) => 
    int index = -1                                               
    for i     = 0 to array.size(this.f_registry.content) - 1             
        if this.f_registry.content.get(i).key == key                         
            index := this.f_registry.content.get(i).val
            this.f_dicts.get(index).set(key,val)
            break                                               

// @function set_int
// @param 	 this	multi_dict
// @param 	 key	string
// @param 	 val	int
// @returns
export method set_int(multi_dict this, string key, int val) => 
    int index = -1                                               
    for i     = 0 to array.size(this.i_registry.content) - 1             
        if this.i_registry.content.get(i).key == key            
            index := this.i_registry.content.get(i).val                      
            this.i_dicts.get(index).set(key,val)                     
            break                                               


// @function set_string
// @param 	 this	multi_dict
// @param 	 key	string
// @param 	 val	string
// @returns
export method set_string(multi_dict this, string key, string val) => 
    int index = -1                                               
    for i     = 0 to array.size(this.s_registry.content) - 1             
        if this.s_registry.content.get(i).key == key            
            index := this.s_registry.content.get(i).val                      
            this.s_dicts.get(index).set(key,val)                     
            break                                               

// @function set_bool
// @param 	 this	multi_dict
// @param 	 key	string
// @param 	 val	bool
// @returns
export method set_bool(multi_dict this, string key, bool val) => 
    int index = -1                                               
    for i     = 0 to array.size(this.s_registry.content) - 1             
        if this.b_registry.content.get(i).key == key            
            index := this.b_registry.content.get(i).val                      
            this.b_dicts.get(index).set(key,val)                     
            break                                               
