hashmaps
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © marspumpkin

import marspumpkin/pson/7

//@version=5
// @description: Simple hashmap implementation for pinescript. It gets your string array and transforms it into a hashmap. Before using it you need to initialize your array with the size you need for your specific case. 
library("hashmaps")

char_at(i) =>
    str.pos(' !#$%&\()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~;', i)    

hashcode_to_index(arr, hashcode) =>
    hashcode % array.size(arr)

hashcode(key) =>
    hash = 7
    length = str.length(key)
    for i = 0 to length - 1 by 1
        hash := hash * 31 + char_at(str.substring(key, i, i+1))
    
    hash

index(arr, key) =>
    hashcode_to_index(arr, hashcode(key))

export contains(string[]arr, string key) =>
    if array.size(arr) > 0
        val = pson.get(array.get(arr, index(arr, key)), key)
        val != '' or val != na
    else
        false
        
export put(string[]arr, string key, string value) =>
    if array.size(arr) > 0
        index = index(arr, key)
        pson_string = array.get(arr, index)
        if pson_string == na
            pson_string := '{}'
        
        // handling collisions
        if pson.exists(pson_string, key)
            pson_string := pson.set(pson_string, key, value)
        else
            pson_string := pson.add(pson_string, key, value)
            
        array.set(arr, index, pson_string)

export put(string[]arr, string key, float value) =>
    put(arr, key, str.tostring(value))
    
export put(string[]arr, string key, int value) =>
    put(arr, key, str.tostring(value))
    
export put(string[]arr, string key, bool value) =>
    put(arr, key, str.tostring(value))
    
export remove(string[]arr, string key) =>
    if array.size(arr) > 0
        index = index(arr, key)
        pson_string = array.get(arr, index)
        
        // collisions
        if pson.exists(pson_string, key)
            removed = pson.remove(pson_string, key)
            pson_string := removed == '{}' ? na : removed
            
        array.set(arr, index, pson_string)

// all get methods will return NaN if key doesn't exist or if the array is empty
export get(string[]arr, string key) => 
    if array.size(arr) > 0
        pson.get(array.get(arr, index(arr, key)), key)

export get_float(string[]arr, string key) =>
    if array.size(arr) > 0
        str.tonumber(pson.get(array.get(arr, index(arr, key)), key))

export get_int(string[]arr, string key) =>
    math.round(get_float(arr, key))
 
export get_bool(string[]arr, string key) =>
    if array.size(arr) > 0
        pson.get(array.get(arr, index(arr, key)), key) == 'true'  

if barstate.islast
    hashmap = array.new_string(100000)
    
    key = 'apple123'
    put(hashmap, key, 'test')
    
    before = get(hashmap, key)
    remove(hashmap, key)
    after = get(hashmap, key)
    after := after == na ? 'NaN' : after
    
    label.new(x=bar_index, y=high, textcolor=color.yellow, text='before removing: ' + before + ', after removing: ' + after, size=size.normal, style=label.style_none)
    
plot(close)