map
Maps in Koto are associative containers of keys mapped to values.
The order in which items are added to the map will be preserved.
Creating a map
There are two ways to directly create a map in Koto: map blocks, and inline maps.
Block map syntax
Maps can be created with indented blocks, where each line contains an entry of
the form Key: Value.
x =
hello: -1
goodbye: 99
x.hello
# -1
x.goodbye
# 99
Nested Maps can be defined with additional indentation:
x =
hello:
world: 99
everybody: 123
to:
you: -1
x.hello.world
# 99
x.hello.to.you
# 123
Inline map syntax
Maps can also be created with curly braces, with comma-separated entries.
x = {hello: -1, goodbye: "abc"}
x.hello
# -1
x.goodbye
# abc
If only the key is provided for an entry, then a value matching the name of the key is looked for and is then copied into the entry.
hello = "abc"
goodbye = 99
x = {hello, goodbye, tschüss: 123}
x.goodbye
# 99
Keys
When creating a Map directly, the keys are defined as strings.
To use non-string values as keys, map.insert can be used.
x = {}
x.insert 0, "Hello"
x.insert true, "World"
"{}, {}!".format x.get(0), x.get(true)
# Hello, World!
Instance functions
When a Function is used as a value in a Map, and if it uses the keyword self
as its first argument, then the runtime will pass the instance of the map that
contains the function as the self argument.
x =
# Initialize an empty list
data: []
# Takes a value and adds it to the list
add_to_data: |self, n| self.data.push n
# Returns the sum of the list
sum: |self| self.data.sum()
x.add_to_data 2
x.add_to_data 20
x.sum()
# 22
Operators
The + operator can be used to merge two maps together.
x = {hello: 123}
y = {goodbye: 99}
x + y
# {hello, goodbye}
Meta Maps and overloaded operations
Maps can be used to create value types with custom behaviour.
Keys with @ prefixes go into the map's 'meta map',
which is checked when the map is encountered in operations.
make_x = |n|
data: n
# Overloading the addition operator
@+: |self, other|
# a new instance is made with the result of adding the two values together
make_x self.data + other.data
# Overloading the subtraction operator
@-: |self, other|
make_x self.data - other.data
x1 = make_x 10
x2 = make_x 20
(x1 + x2).data
# 30
(x1 - x2).data
# -10
All binary operators can be overloaded following this pattern.
Additionally, the following meta functions can customize object behaviour:
@negate- Overloads the unary negation operator:
@negate: |self| make_x -self.data
- Overloads the unary negation operator:
@index- Overloads
[]indexing:@index: |self, index| self.data + index
- Overloads
@display- Customizes how the map will be displayed when formatted as a string:
@display: |self| "X: {}".format self.data
- Customizes how the map will be displayed when formatted as a string:
@type- Provides a String that's used when checking the map's type:
@type: "X"
- Provides a String that's used when checking the map's type:
Meta entries
@meta can be used as a prefix on a map entry to add it to the meta map.
The entry will be accessible on value lookups but won't show up in the regular
map data:
make_x = |n|
data: n
# Overloading the addition operator
@meta get_data: |self| self.data
x = make_x 42
x.keys().to_list()
# ["data"]
x.get_data()
# 42
Tests
Tests are also stored in the meta map, see test.md for info.
Reference
- clear
- contains_key
- copy
- deep_copy
- get
- get_index
- insert
- is_empty
- iter
- keys
- remove
- size
- sort
- update
- values
clear
|Map| -> ()
Clears the map by removing all of its elements.
Example
x = {x: -1, y: 42}
x.clear()
x
# {}
contains_key
|Map, Key| -> Bool
Returns true if the map contains a value with the given key,
and false otherwise.
copy
|Map| -> Map
Makes a unique copy of the map data.
Note that this only copies the first level of data, so nested containers
will share their data with their counterparts in the copy. To make a copy where
any nested containers are also unique, use map.deep_copy.
Example
x = {foo: -1, bar: 99}
y = x
y.foo = 42
x.foo
# 42
z = x.copy()
z.bar = -1
x.bar # x.bar remains unmodified due to the
# 99
See also
deep_copy
|Map| -> Map
Makes a unique deep copy of the map data.
This makes a unique copy of the map data, and then recursively makes deep copies of any nested containers in the map.
If only the first level of data needs to be made unique, then use
map.copy.
Example
x = {foo: 42, bar: {baz: 99}}
y = m.deep_copy()
y.bar.baz = 123
x.bar.baz # a deep copy has been made, so x is unaffected by the change to y
# 99
See also
get
|Map, Key| -> Value
Returns the value corresponding to the given key, or () if the map doesn't
contain the key.
Example
x = {hello: -1}
x.get "hello"
# -1
x.get "goodbye"
# ()
x.insert 99, "xyz"
x.get 99
# xyz
See also
get_index
|Map, Number| -> Tuple
Returns the entry at the given index as a key/value tuple, or () if the map
doesn't contain an entry at that index.
An error will be thrown if a negative index is provided.
Example
x = {foo: -1, bar: -2}
x.get_index 1
# (bar, -2)
x.get_index 99
# ()
See also
insert
|Map, Key| -> Value
Inserts () into the map with the given key.
|Map, Key, Value| -> Value
Inserts a value into the map with the given key.
If the key already existed in the map, then the old value is returned.
If the key didn't already exist, then () is returned.
Example
x = {hello: -1}
x.insert "hello", 99 # -1 already exists at `hello`, so it's returned here
# -1
x.hello # hello is now 99
# 99
x.insert "goodbye", 123 # No existing value at `goodbye`, so () is returned
# ()
x.goodbye
# 123
See also
is_empty
|Map| -> Bool
Returns true if the map contains no entries, otherwise false.
Example
{}.is_empty()
# true
{hello: -1}.is_empty()
# false
See also
iter
|Map| -> Iterator
Returns an iterator that iterates over the map's entries.
Each key/value pair is provided in order as a tuple.
Maps are iterable, so it's not necessary to call .iter() to get access to
iterator operations, but it can be useful sometimes to make a standalone
iterator for manual iteration.
Example
m =
hello: -1
goodbye: 99
x = m.iter();
x.next()
# ("hello", -1)
x.next()
# ("goodbye", 99)
x.next()
# ()
See also
keys
|Map| -> Iterator
Returns an iterator that iterates in order over the map's keys.
Example
m =
hello: -1
goodbye: 99
x = m.keys()
x.next()
# "hello"
x.next()
# "goodbye"
x.next()
# ()
See also
remove
|Map, Key| -> Value
Removes the entry that matches the given key.
If the entry existed then its value is returned, otherwise () is returned.
Example
x =
hello: -1
goodbye: 99
x.remove "hello"
# -1
x.remove "xyz"
# ()
x.remove "goodbye"
# 99
x.is_empty()
# true
See also
size
|Map| -> Number
Returns the number of entries contained in the map.
Example
{}.size()
# 0
{"a": 0, "b": 1}.size()
# 2
See also
sort
|Map| -> ()
Sorts the map's entries by key.
|Map, |Value, Value| -> Value| -> ()
Sorts the map's entries, based on the output of calling a 'key' function for each entry. The entry's key and value are passed into the function as separate arguments.
The function result is cached, so it's only called once per entry.
Example
x =
hello: 123
bye: -1
tschüss: 99
x.sort() # Sorts the map by key
x
# {bye, hello, tschüss}
x.sort |_, value| value # Sort the map by value
x
# {bye, tschüss, hello}
x.sort |key, _| -key.size() # Sort the map by reversed key length
x
# {tschüss, hello, bye}
update
|Map, Key, |Value| -> Value| -> Value
Updates the value associated with a given key by calling a function with either
the existing value, or () if it doesn't there isn't a matching entry.
The result of the function will either replace an existing value, or if no value existed then an entry with the given key will be inserted into the map with the function's result.
The function result is then returned from update.
|Map, Key, Value, |Value| -> Value| -> Value
This variant of update takes a default value that is provided to the
function if a matching entry doesn't exist.
Example
x =
hello: -1
goodbye: 99
x.update "hello", |n| n * 2
# -2
x.hello
# -2
x.update "tschüss", 10, |n| n * 10
# 100
x.tschüss
# 100
See also
values
|Map| -> Iterator
Returns an iterator that iterates in order over the map's values.
Example
m =
hello: -1
goodbye: 99
x = m.values()
x.next()
# -1
x.next()
# 99
x.next()
# ()