example/type.dict.primi

Summary

Maintainability
Test Coverage
import ._helpers: assert_error

_ = "Default value for dict is {}"
default = dict()
assert(default == {}, _)

a = {0: 'a', true: 'b'}
assert(len(a) == 2, "Dict reports correct length")

b = { // Can be multiline
    true: 'b',
    0: 'a', // With comments.
}

_ = "Simple dicts with different order but same 'key: value' pairs are the same"
assert(a == b, _)

a = {
    0: 'a',
    true: 'b',
    1: '111',
    false: 'c',
    null: 'd',
    '0': 'g',
    '00': 'h',
    00: 'overwrites_first_key', // Because integer 00 == 0
}
assert(len(a) == 7, "Dict reports correct length")

_ = "Correct result of dict_get function"
assert(a.get(0) == 'overwrites_first_key', _)
assert(a.get(true) == 'b', _)
assert(a.get(1) == '111', _)
assert(a.get(false) == 'c', _)
assert(a.get(null) == 'd', _)
assert(a.get('0') == 'g', _)
assert(a.get('00') == 'h', _)

b = {
    false: 'c',
    '0': 'g',
    '00': 'h',
    0: 'a',
    00: 'overwrites_first_key', // Because integer 00 == 0
    true: 'b',
    1: '111',
    null: 'd',
}
assert(len(b) == 7, "Dict reports correct length")

_ = "Complex dicts with different order but same 'key: value' pairs are the same"
assert(a == b, _)

//
// Modifying dict values.
//

b[1] = '112'
assert(b[1] == '112', "Dicts can have values changed")
assert(a != b, "After changing value of one dict, dicts are not the same")
assert(b.get('completely_new_key') == null)

b['completely_new_key'] = {'i_think': 'wow!'}
assert(b.get('completely_new_key') == {'i_think': 'wow!'})

_ = "Modifying dict values: References are kept."
new_dict = {}
b['some_new_dict'] = new_dict
new_dict['new_key'] = 'new_value'
assert(b['some_new_dict']['new_key'] == 'new_value', _)

//
// Extreme number keys.
//

SUPER_BIG_NUMBER_INT = 1223456 ** 789
SUPER_BIG_NUMBER_FLOAT = 1223456 ** 789 / (10 ** 100)
SUPER_SMALL_NUMBER_FLOAT = 1 / 10 ** 128
SUPER_SMALL_NUMBER_FLOAT_TOOSMALL = 1 / 10 ** 129 // Will be exactly zero.

c = {
    0000000000000000.0000000000000000000000001: 'first',
    0.0000000000000000000000001: 'second',
    00000000000000010000000000000000.0000000000000000000000001: 'first',
    10000000000000000.0000000000000000000000001: 'second',
    SUPER_BIG_NUMBER_INT: 'bigint1',
    SUPER_BIG_NUMBER_INT + 1: 'bigint2',
    SUPER_BIG_NUMBER_INT - 1: 'bigint3',
    SUPER_BIG_NUMBER_FLOAT: 'bigfloat1',
    SUPER_BIG_NUMBER_FLOAT + 1: 'bigfloat2',
    SUPER_BIG_NUMBER_FLOAT - 1: 'bigfloat3',
    SUPER_SMALL_NUMBER_FLOAT: 'smallfloat1',
    SUPER_SMALL_NUMBER_FLOAT + 1: 'smallfloat2',
    SUPER_SMALL_NUMBER_FLOAT - 1: 'smallfloat3',
    SUPER_SMALL_NUMBER_FLOAT_TOOSMALL: 'toosmall_first',
    0: 'toosmall_second',
}

_ = 'Dictionaries can have really large integer numbers as keys without collisions'
assert(c[SUPER_BIG_NUMBER_INT] == 'bigint1', _)
assert(c[SUPER_BIG_NUMBER_INT + 1] == 'bigint2', _)
assert(c[SUPER_BIG_NUMBER_INT - 1] == 'bigint3', _)

_ = 'Dictionaries can have really large float numbers as keys without collisions'
assert(c[SUPER_BIG_NUMBER_FLOAT] == 'bigfloat1', _)
assert(c[SUPER_BIG_NUMBER_FLOAT + 1] == 'bigfloat2', _)
assert(c[SUPER_BIG_NUMBER_FLOAT - 1] == 'bigfloat3', _)

_ = 'Dictionaries can have really small float numbers as keys with up to 128 decimal digits without collisions'
assert(c[SUPER_SMALL_NUMBER_FLOAT] == 'smallfloat1', _)
assert(c[SUPER_SMALL_NUMBER_FLOAT + 1] == 'smallfloat2', _)
assert(c[SUPER_SMALL_NUMBER_FLOAT - 1] == 'smallfloat3', _)

_ = 'All values containing word "first" are overwritten by other keys that normalized into collisions'
for (value in c.values()) {
    assert(value.contains('first') == false, _)
}

empty_dict = {}
assert(true == empty_dict.get('_', true))
assert(false == empty_dict.get('_', false))
assert([] == empty_dict.get('_', []))
assert({} == empty_dict.get('_', {}))
assert({'x': 123, 'y': 456} == empty_dict.get('_', {'y': 456, 'x': 123}))

//
// Errors.
//

_ = "Getting nonexistent key directly from dict results in an error"
assert_error(() => {
    print(empty_dict['doesn\'t exist'])
}, _)

_ = "Inserting value into dict without specified key results in an error"
assert_error(() => {
    empty_dict[] = 'OH NO!'
}, _)

// Testing a large dict with 500_000 items.
// May be a bit slow for common tests, so uncomment this if needed.
//
// initial_memory = memory_get_usage()
//
// large_dict = {}
// c = 0
// while (c <= 500_000) {
//     large_dict[c] = hash_md5(c)
//     c = c + 1
// }
// assert(len(large_dict) == 500_000)
//
// print('Large dict needed {} MB of memory'.format((memory_get_usage() - initial_memory) / 10**6)

//
// Dict methods.
//

some_dict = {
    'key_a': 'a',
    'key_b': 'b',
    'key_c': 1,
    'key_d': {'inner_key_a': 123, 'inner_key_b': 456},
    '10': ['a'],
    true: 'true_value',
    false: 'false_value',
}

d_values = some_dict.values()

// Deprecated "reverse" method.
// _ = "Reversed dict equals to the original dict, regardless of their order"
// rev_dict = some_dict.reverse()
// assert(some_dict == rev_dict, _)
//
// _ = "Reversed dict really is reversed"
// rev_d_values = rev_dict.values()
// assert(d_values[0] != rev_d_values[0])

_ = "dict.values() function works"
assert(['a', 'b', 1, {'inner_key_a': 123, 'inner_key_b': 456}, ['a'], 'true_value', 'false_value'] == d_values, _)
// assert(['false_value', 'true_value', ['a'], {'inner_key_a': 123, 'inner_key_b': 456}, 1, 'b', 'a'] == rev_d_values, _)

_ = "dict.keys() function works"
assert(['key_a', 'key_b', 'key_c', 'key_d', '10', true, false] == some_dict.keys(), _)
// assert([false, true, '10', 'key_d', 'key_c', 'key_b', 'key_a'] == some_dict.reverse().keys(), _)

_ = "dict.has_key() function works"
assert(some_dict.has_key('xxx') == false, _)
assert(some_dict.has_key(null) == false, _)
assert(some_dict.has_key(true), _)
assert(some_dict.has_key('key_a'), _)

_ = "dict key types are handled properly - numbers are not strings"
assert(some_dict.has_key(10) == false, _)
assert(some_dict.has_key('10'), _)

_ = "dict.has_value() function works"
assert(some_dict.has_value('nope_not_there') == false, _)
assert(some_dict.has_value('aa') == false, _)
assert(some_dict.has_value([]) == false, _)
assert(some_dict.has_value({}) == false, _)
assert(some_dict.has_value({'inner_key_a': 123, 'inner_key_b': 456}), _)
assert(some_dict.has_value(1), _)
assert(some_dict.has_value('a'), _)
assert(some_dict.has_value('b'), _)

_ = "dict.map() returns a new dict without modifying the original"
result = some_dict.map((v, k) => {
    return "value:{}".format(v)
})
assert(type(result) == dict, _ + " #1")
assert(result.values() == [
    "value:a",
    "value:b",
    "value:1",
    'value:{"inner_key_a": 123, "inner_key_b": 456}',
    'value:["a"]',
    "value:true_value",
    "value:false_value",
], _ + " #2")
assert(result.keys() == some_dict.keys(), _ + " #3")
assert(result['key_a'] != some_dict['key_a'], _ + " #4")

_ = "dict.map() function works"
result = []
some_dict.map((val, key) => {

    // Keep only strings.
    if (type(val) == string) {
        result[] = val
    }

})
assert(result == ['a', 'b', 'true_value', 'false_value'])

_ = "dict.map() function works"
result = []
some_dict.map((_, key) => {

    // Note that the underscore variable name will not modify variable with
    // the same name in the outer scope.

    // Keep only string keys.
    if (type(key) == string) {
        result[] = key
    }

})
assert(result == ['key_a', 'key_b', 'key_c', 'key_d', '10'])