Rename Map to Dictionary and Set to Hashset. (#10474)

- Rename `Map` to `Dictionary`.
- Rename `Set` to `Hashset`.
- Add a deprecated place holder for the static method of `Map`.
This commit is contained in:
James Dunkerley 2024-07-09 10:12:23 +01:00 committed by GitHub
parent b2c4559678
commit 4b3e4ae15e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
95 changed files with 1585 additions and 1552 deletions

View File

@ -14,11 +14,13 @@
`Location.Right`.][10445]
- [Renamed `Postgres_Details.Postgres` to `Postgres.Server`.][10466]
- [Remove `First` and `Last` from namespace, use auto-scoped.][10467]
- [Rename `Map` to `Dictionary` and `Set` to `Hashset`.][10474]
[10434]: https://github.com/enso-org/enso/pull/10434
[10445]: https://github.com/enso-org/enso/pull/10445
[10466]: https://github.com/enso-org/enso/pull/10466
[10467]: https://github.com/enso-org/enso/pull/10467
[10474]: https://github.com/enso-org/enso/pull/10474
# Enso 2024.2

View File

@ -96,11 +96,11 @@ read_bucket bucket prefix="" credentials:AWS_Credential=AWS_Credential.Default d
- key: the key of the object.
- credentials: AWS credentials. If not provided, the default credentials will
be used.
head : Text -> Text -> AWS_Credential -> Map Text Any ! S3_Error
head : Text -> Text -> AWS_Credential -> Dictionary Text Any ! S3_Error
head bucket key="" credentials:AWS_Credential=AWS_Credential.Default =
response = raw_head bucket key credentials
pairs = response.sdkFields.map f-> [f.memberName, f.getValueOrDefault response]
Map.from_vector pairs
Dictionary.from_vector pairs
## PRIVATE
Gets the raw metadata of a bucket or object.
@ -109,7 +109,7 @@ head bucket key="" credentials:AWS_Credential=AWS_Credential.Default =
- bucket: the name of the bucket.
- key: the key of the object.
- credentials: AWS credentials.
raw_head : Text -> Text -> AWS_Credential -> Map Text Any ! S3_Error
raw_head : Text -> Text -> AWS_Credential -> Dictionary Text Any ! S3_Error
raw_head bucket key credentials =
client = make_client_for_bucket bucket credentials
case key == "" of

View File

@ -392,8 +392,8 @@ type Any
from Standard.Examples import Example_Error_Type
example_map_error =
my_map = Map.empty
error = my_map.at "x"
my_dictionary = Dictionary.empty
error = my_dictionary.at "x"
error.map_error (_ -> Example_Error_Type "x is missing")
map_error : (Error -> Error) -> Any
map_error self ~f =

View File

@ -310,7 +310,7 @@ fetch (uri:(URI | Text)) (method:HTTP_Method=HTTP_Method.Get) (headers:(Vector (
import Standard.Base.Data
test_file = enso_project.data / "sample.png"
form_data = Map.from_vector [["key", "val"], ["a_file", test_file]]
form_data = Dictionary.from_vector [["key", "val"], ["a_file", test_file]]
response = Data.post url_post (Request_Body.Form_Data form_data)
> Example
@ -318,7 +318,7 @@ fetch (uri:(URI | Text)) (method:HTTP_Method=HTTP_Method.Get) (headers:(Vector (
import Standard.Base.Data
test_file = enso_project.data / "sample.txt"
form_data = Map.from_vector [["key", "val"], ["a_file", test_file]]
form_data = Dictionary.from_vector [["key", "val"], ["a_file", test_file]]
response = Data.post url_post (Request_Body.Form_Data form_data url_encoded=True)
@uri Text_Input
@response_format Data_Read_Helpers.format_widget_with_raw_response

View File

@ -0,0 +1,425 @@
import project.Any.Any
import project.Data.Numbers.Integer
import project.Data.Pair.Pair
import project.Data.Text.Text
import project.Data.Vector.Vector
import project.Error.Error
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Errors.No_Such_Key.No_Such_Key
import project.Nothing.Nothing
import project.Panic.Panic
from project.Data.Boolean import Boolean, False, True
from project.Data.Text.Extensions import all
## A key-value store. It is possible to use any type as keys and values and mix
them in one Dictionary. Keys are checked for equality based on their hash
code and `==` operator, which is both an internal part of Enso. Enso is
capable of computing a hash code, and checking for equality any objects that
can appear in Enso - primitives, Atoms, values coming from different
languages, etc.
For keys that are not reflexive, like `Number.nan`,
[Same Value equality specification](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#same-value-zero_equality)
is used. This means that both `Number.nan` and types with comparators that
violate reflexivity (e.g. their `compare` method always returns `Nothing`)
can be used as keys in the Dictionary.
A single key-value pair is called an *entry*.
It is possible to pass a Dictionary created in Enso to foreign functions,
where it will be treated as appropriate map structures - in Python that is a
dictionary, and in JavaScript, it is a `Map`. And likewise, it is possible
to pass a foreign map into Enso, where it will be treated as a Dictionary.
@Builtin_Type
type Dictionary key value
## PRIVATE
ADVANCED
Returns an empty dictionary.
empty : Dictionary
empty = @Builtin_Method "Dictionary.empty"
## PRIVATE
ADVANCED
Returns a single-element dictionary with the given key and value.
A Call to `Dictionary.singleton key value` is the same as a call to
`Dictionary.empty.insert key value`.
Arguments:
- key: The key to to use for `value` in the dictionary.
- value: The value to store under 'key' in the dictionary.
> Example
Create a single element dictionary storing the key "my_key" and the
value 2.
example_singleton = Dictionary.singleton "my_key" 2
singleton : Any -> Any -> Dictionary
singleton key value = Dictionary.empty.insert key value
## ALIAS dictionary, lookup table
GROUP Constants
ICON convert
Builds a dictionary from two Vectors. The first vector contains the keys,
and the second vector contains the values. The two vectors must be of the
same length.
Arguments:
- keys: A vector of keys.
- values: A vector of values.
- error_on_duplicates: A flag which specifies if duplicate keys on the
input vector should result in an error. By default, set to `True`,
meaning that if two entries in the vector share the same key, an
`Illegal_Argument` error is raised. If set to `False`, the last entry
with a given key will be kept.
from_keys_and_values : Vector Any -> Vector Any -> Boolean -> Dictionary ! Illegal_Argument
from_keys_and_values keys:Vector values:Vector error_on_duplicates:Boolean=True =
if keys.length != values.length then Error.throw (Illegal_Argument.Error "`Dictionary.from_keys_and_values` encountered two vectors of different lengths.") else
keys.fold_with_index Dictionary.empty current-> idx-> key->
if error_on_duplicates.not || (current.contains_key key . not) then current.insert key (values.at idx) else
Error.throw (Illegal_Argument.Error "`Dictionary.from_keys_and_values` encountered duplicate key: "+key.to_display_text)
## ALIAS dictionary, lookup table
GROUP Constants
ICON convert
Builds a dictionary from a vector of key-value pairs, with each key-value
pair represented as a 2 element vector.
Arguments:
- vec: A vector of key-value pairs (2 element vectors).
- error_on_duplicates: A flag which specifies if duplicate keys on the
input vector should result in an error. By default, set to `True`,
meaning that if two entries in the vector share the same key, an
`Illegal_Argument` error is raised. If set to `False`, the last entry
with a given key will be kept.
> Example
Building a dictionary containing two key-value pairs.
example_from_vector = Dictionary.from_vector [["A", 1], ["B", 2]]
from_vector : Vector Any -> Boolean -> Dictionary ! Illegal_Argument
from_vector vec error_on_duplicates=True =
vec.fold Dictionary.empty m-> el-> if el.length != 2 then Error.throw (Illegal_Argument.Error "`Dictionary.from_vector` encountered an invalid value. Each value in the vector has to be a key-value pair - it must have exactly 2 elements.") else
key = el.at 0
value = el.at 1
if error_on_duplicates.not || (m.contains_key key . not) then m.insert key value else
Error.throw (Illegal_Argument.Error "`Dictionary.from_vector` encountered duplicate key: "+key.to_display_text)
## GROUP Logical
ICON metadata
Returns True if the Dictionary is empty, i.e. does not have any entries.
is_empty : Boolean
is_empty self = self.size == 0
## GROUP Logical
ICON metadata
Returns True if the Dictionary is not empty, i.e. has at least one entry.
not_empty : Boolean
not_empty self = self.is_empty.not
## GROUP Metadata
ICON metadata
Returns the number of entries in this dictionary.
size : Integer
size self = @Builtin_Method "Dictionary.size"
## GROUP Metadata
ICON metadata
Returns the number of entries in this dictionary.
length : Integer
length self = self.size
## GROUP Calculations
ICON row_add
Inserts a key-value mapping into this dictionary, overriding any existing
instance of `key` with the new `value`.
Note that since the return type is also a `Dictionary`, multiple `insert`
calls can be chained, e.g., `dictionary.insert "A" 1 . insert "B" 2`.
Due to the limitation of the current implementation, inserts with a
key that is already contained in the dictionary, or insert on a
dictionary instance that is re-used in other computations, have a linear
time complexity. For all the other cases, the time complexity of this
method is constant.
Arguments:
- key: The key to insert the value for.
- value: The value to associate with the `key`.
> Example
Insert the value "seven" into the dictionary for the key 7.
import Standard.Examples
example_insert = Examples.dictionary.insert 7 "seven"
insert : Any -> Any -> Dictionary
insert self key value = @Builtin_Method "Dictionary.insert"
## GROUP Selections
ICON table_clean
Removes an entry specified by the given key from this dictionary, and
returns a new dictionary without this entry. Throw `No_Such_Key.Error` if
`key` is not present.
Arguments:
- key: The key to look up in the dictionary.
> Example
Remove key "A" from a dictionary
import Standard.Examples
Examples.dictionary.remove "A"
remove : Any -> Dictionary ! No_Such_Key
remove self key =
Panic.catch Any (self.remove_builtin key) _->
Error.throw (No_Such_Key.Error self key)
## GROUP Selections
ICON parse3
Gets the value associated with `key` in this dictionary, or throws a
`No_Such_Key.Error` if `key` is not present.
This method has a constant time complexity.
Arguments:
- key: The key to look up in the dictionary.
> Example
Looks up the value for the key "A" in a dictionary.
import Standard.Examples
example_at = Examples.dictionary.at "A"
at : Any -> Any ! No_Such_Key
at self key = self.get key (Error.throw (No_Such_Key.Error self key))
## ICON parse3
Gets the value associated with `key` in this dictionary, or returns
`if_missing` if it isn't present.
This method has a constant time complexity.
Arguments:
- key: The key to look up in the dictionary.
- if_missing: The value to use if the key isn't present.
> Example
Get the value for the key 2 in a dictionary or instead return "zero" if it
isn't present.
import Standard.Examples
example_get = Examples.dictionary.get 2 "zero"
get : Any -> Any -> Any
get self key ~if_missing=Nothing = self.get_builtin key if_missing
## GROUP Logical
ICON preparation
Returns True iff the Dictionary contains the given `key`.
contains_key : Any -> Boolean
contains_key self key = @Builtin_Method "Dictionary.contains_key"
## GROUP Selections
ICON select_column
Returns an unsorted vector of all the keys in this Dictionary.
keys : Vector Any
keys self = self.to_vector.map pair-> pair.at 0
## GROUP Selections
ICON select_column
Returns an unsorted vector of all the values in this Dictionary.
values : Vector Any
values self = self.to_vector.map pair-> pair.at 1
## ICON column_add
Maps a function over each value in this dictionary.
Arguments:
- function: The function to apply to each value in the dictionary, taking
a value and returning a value.
> Example
Append "_word" to all values in the dictionary.
import Standard.Examples
example_map = Examples.dictionary.map (+ "_word")
map : (Any -> Any) -> Dictionary
map self function =
kv_func = _ -> function
self.map_with_key kv_func
## ICON column_add
Maps a function over each key-value pair in the dictionary, transforming
the value.
Arguments:
- function: Function to apply to each key and value in the dictionary,
taking a key and a value and returning a value.
> Example
Prepend the keys to the values in the dictionary.
import Standard.Examples
example_map_with_key =
Examples.dictionary.map_with_key (k -> v -> k.to_text + "-" + v)
map_with_key : (Any -> Any -> Any) -> Dictionary
map_with_key self function =
Dictionary.from_vector <| self.to_vector.map pair->
key = pair.first
value = pair.last
[key, (function key value)]
## ICON column_add
Maps a function over each key in this dictionary.
Arguments:
- function: The function to apply to each key in the dictionary, taking a
key and returning a new key.
> Example
Doubling all keys in the dictionary.
import Standard.Examples
example_map_keys = Examples.dictionary.map_keys *2
map_keys : (Any -> Any) -> Dictionary
map_keys self function =
trans_function = k -> v -> [function k, v]
self.transform trans_function
## ICON column_add
Transforms the map's keys and values to create a new dictionary.
Arguments:
- function: The function used to transform the dictionary, taking a key
and a value and returning a pair of `[key, value]`.
! Error Conditions
- If multiple dictionary entries end up with duplicate keys after the
transformation, an `Illegal_Argument.Error` is thrown.
> Example
Turn all keys into `Text` and append "_word" to the values in the
dictionary.
import Standard.Examples
example_transform =
Examples.dictionary.transform (k -> v -> [k.to_text, v + "_word"])
transform : (Any -> Any -> [Any, Any]) -> Dictionary
transform self function =
func_pairs = p -> function (p.at 0) (p.at 1)
vec_transformed = self.to_vector.map func_pairs
new_dictionary = Dictionary.from_vector vec_transformed error_on_duplicates=True
new_dictionary.catch Illegal_Argument error->
case error.message.starts_with "`Dictionary.from_vector` encountered duplicate key" of
True ->
new_message = error.message.replace "from_vector" "transform"
Error.throw (Illegal_Argument.Error new_message error.cause)
False -> new_dictionary
## ICON transform4
Combines the values in the dictionary.
Arguments:
- init: The initial value for the fold.
- function: A binary function to apply to pairs of values.
> Example
Find the length of the longest word in the dictionary.
import Standard.Examples
example_fold = Examples.dictionary.fold 0 (l -> r -> l.max r.length)
fold : Any -> (Any -> Any -> Any) -> Any
fold self init function = self.values.fold init function
## ICON transform4
Combines the key-value pairs in the dictionary.
Arguments:
- init: The initial value for the fold.
- function: A function taking the left value, the current key, and the
current value, and combining them to yield a single value.
> Example
Glue the values in the dictionary together with the keys.
import Standard.Examples
example_fold_with_key =
Examples.dictionary.fold_with_key "" (l -> k -> v -> l + k.to_text + v)
fold_with_key : Any -> (Any -> Any -> Any -> Any) -> Any
fold_with_key self init function =
self.to_vector.fold init acc-> pair->
function acc pair.first pair.last
## PRIVATE
ADVANCED
Applies a function to each value in the dictionary.
Arguments:
- function: The function to apply to each value in the dictionary, taking
a value and returning anything.
This method does not return the results, so it is only useful for performing
computations with side-effects.
If the function returns a dataflow error, the error is converted to a
panic and thrown immediately stopping further processing.
> Example
Printing each value in the dictionary.
import Standard.Examples
example_each = Examples.dictionary.each IO.println
each : (Any -> Any) -> Nothing
each self function =
kv_func = _ -> function
self.each_with_key kv_func
## PRIVATE
ADVANCED
Applies a function to each key-value pair in the dictionary.
Arguments:
- function: The function to apply to each key-value pair in the
dictionary, taking a key and a value and returning anything.
This method does not return the results, so it is only useful for performing
computations with side-effects.
> Example
Printing each key and value in the dictionary.
import Standard.Examples
example_each_with_key = Examples.dictionary.each_with_key k->v->
IO.println k
IO.println v
each_with_key : (Any -> Any -> Any) -> Nothing
each_with_key self function =
self.to_vector.each pair->
function pair.first pair.last
## GROUP Conversions
ICON convert
Returns an unsorted vector of key-value pairs (nested 2 element vectors).
`Dictionary.from_vector` method is an inverse method, so the following
expression is true for all dictionaries:
`Dictionary.from_vector dictionary.to_vector == dictionary`.
to_vector : Vector Any
to_vector self = @Builtin_Method "Dictionary.to_vector"
## PRIVATE
Returns a text representation of this Dictionary.
to_text : Text
to_text self = @Builtin_Method "Dictionary.to_text"
## PRIVATE
get_builtin : Any -> Any -> Any
get_builtin self key ~if_missing = @Builtin_Method "Dictionary.get_builtin"

View File

@ -1,7 +1,7 @@
import project.Any.Any
import project.Data.Hashset.Hashset
import project.Data.Locale.Locale
import project.Data.Numbers.Number
import project.Data.Set.Set
import project.Data.Text.Case_Sensitivity.Case_Sensitivity
import project.Data.Text.Regex.Regex
import project.Data.Text.Text
@ -198,9 +198,7 @@ type Filter_Condition
Like sql_pattern _ ->
regex = sql_like_to_regex sql_pattern
handle_nothing <| regex.matches
Is_In values _ ->
set = Set.from_vector values
set.contains
Is_In values _ -> Hashset.from_vector values . contains
if self.action == Filter_Action.Keep then base else v -> (base v).not
## PRIVATE

View File

@ -1,6 +1,6 @@
import project.Any.Any
import project.Data.Array_Proxy.Array_Proxy
import project.Data.Map.Map
import project.Data.Dictionary.Dictionary
import project.Data.Numbers.Integer
import project.Data.Ordering.Comparable
import project.Data.Ordering.Ordering
@ -13,9 +13,9 @@ from project.Data.Boolean import Boolean, False, True
from project.Data.Text.Extensions import all
## An unordered collection of unique values.
type Set
type Hashset
## PRIVATE
Value (underlying_map : Map Any Nothing)
Value (underlying_dictionary : Dictionary Any Nothing)
## PRIVATE
ADVANCED
@ -28,30 +28,30 @@ type Set
occurrence of each duplicated element is retained in the set. If set to
`True` it will raise an `Illegal_Argument` if duplicate elements are
encountered.
from_vector : Vector Any -> Boolean -> Set ! Illegal_Argument
from_vector : Vector Any -> Boolean -> Hashset ! Illegal_Argument
from_vector (vector : Vector) (error_on_duplicates : Boolean = False) =
pairs_array = Array_Proxy.new vector.length (i-> [vector.at i, Nothing])
pairs = Vector.from_polyglot_array pairs_array
map = Map.from_vector pairs error_on_duplicates=error_on_duplicates
Set.Value map
dictionary = Dictionary.from_vector pairs error_on_duplicates=error_on_duplicates
Hashset.Value dictionary
## PRIVATE
ADVANCED
Constructs an empty set.
empty : Set
empty = Set.Value Map.empty
empty : Hashset
empty = Hashset.Value Dictionary.empty
## GROUP Conversions
ICON convert
Returns a vector containing all elements of this set.
to_vector : Vector
to_vector self = self.underlying_map.keys
to_vector self = self.underlying_dictionary.keys
## GROUP Metadata
ICON metadata
Returns the number of elements in this set.
size : Integer
size self = self.underlying_map.size
size self = self.underlying_dictionary.size
## GROUP Metadata
ICON metadata
@ -63,19 +63,19 @@ type Set
ICON metadata
Checks if the set is empty.
is_empty : Boolean
is_empty self = self.underlying_map.is_empty
is_empty self = self.underlying_dictionary.is_empty
## GROUP Logical
ICON metadata
Checks if the set is not empty.
not_empty : Boolean
not_empty self = self.underlying_map.not_empty
not_empty self = self.underlying_dictionary.not_empty
## GROUP Logical
ICON preparation
Checks if this set contains a given value.
contains : Any -> Boolean
contains self value = self.underlying_map.contains_key value
contains self value = self.underlying_dictionary.contains_key value
## GROUP Logical
ICON preparation
@ -103,48 +103,48 @@ type Set
GROUP Calculations
ICON row_add
Adds a value to this set.
insert : Any -> Set
insert : Any -> Hashset
insert self value =
new_map = self.underlying_map.insert value Nothing
Set.Value new_map
dictionary = self.underlying_dictionary.insert value Nothing
Hashset.Value dictionary
## GROUP Calculations
ICON union
Creates a union of the two sets.
union : Set -> Set
union self (other : Set) =
start_map = self.underlying_map
new_map = other.to_vector.fold start_map m-> el-> m.insert el Nothing
Set.Value new_map
union : Hashset -> Hashset
union self (other : Hashset) =
start_dictionary = self.underlying_dictionary
dictionary = other.to_vector.fold start_dictionary m-> el-> m.insert el Nothing
Hashset.Value dictionary
## GROUP Calculations
ICON join
Creates an intersection of the two sets.
intersection : Set -> Set
intersection self (other : Set) =
other_map = other.underlying_map
new_map = self.underlying_map.keys.fold Map.empty m-> el->
if other_map.contains_key el then m.insert el Nothing else m
Set.Value new_map
intersection : Hashset -> Hashset
intersection self (other : Hashset) =
other_dictionary = other.underlying_dictionary
dictionary = self.underlying_dictionary.keys.fold Dictionary.empty m-> el->
if other_dictionary.contains_key el then m.insert el Nothing else m
Hashset.Value dictionary
## ICON join
Computes a set difference.
Returns the set that contains all elements of this set that are not in
the other set.
difference : Set -> Set
difference self (other : Set) =
other_map = other.underlying_map
new_map = self.underlying_map.keys.fold Map.empty m-> el->
if other_map.contains_key el then m else m.insert el Nothing
Set.Value new_map
difference : Hashset -> Hashset
difference self (other : Hashset) =
other_dictionary = other.underlying_dictionary
dictionary = self.underlying_dictionary.keys.fold Dictionary.empty m-> el->
if other_dictionary.contains_key el then m else m.insert el Nothing
Hashset.Value dictionary
## PRIVATE
to_text : Text
to_text self = self.to_vector.map .pretty . join ", " "Set{" "}"
to_text self = self.to_vector.map .pretty . join ", " "Hashset{" "}"
## PRIVATE
type Set_Comparator
type Hashset_Comparator
## PRIVATE
compare x y =
if x.size != y.size then Nothing else

View File

@ -2,7 +2,7 @@ import project.Any.Any
import project.Data.Array.Array
import project.Data.Array_Proxy.Array_Proxy
import project.Data.Decimal.Decimal
import project.Data.Map.Map
import project.Data.Dictionary.Dictionary
import project.Data.Numbers.Float
import project.Data.Numbers.Integer
import project.Data.Numbers.Number
@ -252,13 +252,13 @@ type JS_Object
## GROUP Logical
ICON metadata
Returns True iff the Map is empty, i.e., does not have any entries.
Returns True if the JS_Object is empty, i.e., does not have any entries.
is_empty : Boolean
is_empty self = self.length == 0
## GROUP Logical
ICON metadata
Returns True iff the Map is not empty, i.e., has at least one entry.
Returns True if the JS_Object is not empty, i.e., has at least one entry.
not_empty : Boolean
not_empty self = self.is_empty.not
@ -304,10 +304,10 @@ type JS_Object
Creates an Enso object from the JS_Object.
into : Any -> Any
into self target_type = case target_type of
JS_Object -> self
Vector -> self.to_vector
Map -> Map.from_vector self.to_vector
_ ->
JS_Object -> self
Vector -> self.to_vector
Dictionary -> Dictionary.from_vector self.to_vector
_ ->
## First try a conversion
Panic.catch No_Such_Conversion (self.to target_type) _->
## If that fails, try to construct the type

View File

@ -2,10 +2,10 @@ import project.Any.Any
import project.Data.Array.Array
import project.Data.Array_Proxy.Array_Proxy
import project.Data.Decimal.Decimal
import project.Data.Dictionary.Dictionary
import project.Data.Json.JS_Object
import project.Data.Json.Json
import project.Data.Locale.Locale
import project.Data.Map.Map
import project.Data.Numbers.Float
import project.Data.Numbers.Integer
import project.Data.Numbers.Number
@ -182,10 +182,10 @@ Locale.to_js_object self =
For Map, this is serialized as a Vector of Key-Value pairs.
Enso Maps support arbitrary types as map keys, so we cannot serialize them into JS Objects because there only strings are accepted as keys.
Map.to_js_object : JS_Object
Map.to_js_object self =
map_vector = self.to_vector
map_vector.map p-> [p.first.to_js_object, p.second.to_js_object]
Dictionary.to_js_object : JS_Object
Dictionary.to_js_object self =
as_vector = self.to_vector
as_vector.map p-> [p.first.to_js_object, p.second.to_js_object]
## PRIVATE
ICON convert

View File

@ -1,436 +1,37 @@
import project.Any.Any
import project.Data.Numbers.Integer
import project.Data.Pair.Pair
import project.Data.Text.Text
import project.Data.Vector.Vector
import project.Error.Error
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Errors.No_Such_Key.No_Such_Key
import project.Nothing.Nothing
import project.Panic.Panic
import project.Errors.Deprecated.Deprecated
from project.Data.Boolean import Boolean, False, True
from project.Data.Text.Extensions import all
## A key-value store. It is possible to use any type as keys and values and mix them in
one Map. Keys are checked for equality based on their hash code and `==` operator, which
is both an internal part of Enso. Enso is capable of computing a hash code, and checking
for equality any objects that can appear in Enso - primitives, Atoms, values coming from
different languages, etc.
For keys that are not reflexive, like `Number.nan`,
[Same Value equality specification](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#same-value-zero_equality)
is used. This means that both `Number.nan` and types with comparators that violate
reflexivity (e.g. their `compare` method always returns `Nothing`) can be used as keys
in the Map.
A single key-value pair is called an *entry*.
It is possible to pass a Map created in Enso to foreign functions, where it will be treated
as appropriate map structures - in Python that is a dictionary, and in JavaScript, it is
a `Map`. And likewise, it is possible to pass a foreign map into Enso, where it will be
treated as a Map.
@Builtin_Type
## PRIVATE
Deprecate place holder for the Map type.
type Map key value
## PRIVATE
ADVANCED
Returns an empty map.
empty : Map
empty = @Builtin_Method "Map.empty"
DEPRECATED Use Dictionary.empty instead.
empty : Any ! Deprecated
empty =
Error.throw (Deprecated.Warning "Standard.Base.Data.Map.Map" "empty" "Deprecated: `Map.empty` has been replaced by `Dictionary.empty`.")
## PRIVATE
ADVANCED
Returns a single-element map with the given key and value.
A Call to `Map.singleton key value` is the same as a call to
`Map.empty.insert key value`.
DEPRECATED Use Dictionary.singleton instead.
singleton : Any -> Any -> Any ! Deprecated
singleton key value =
_ = [key, value]
Error.throw (Deprecated.Warning "Standard.Base.Data.Map.Map" "singleton" "Deprecated: `Map.singleton` has been replaced by `Dictionary.singleton`.")
Arguments:
- key: The key to to use for `value` in the map.
- value: The value to store under 'key' in the map.
> Example
Create a single element map storing the key "my_key" and the value 2.
import Standard.Base.Data.Map.Map
example_singleton = Map.singleton "my_key" 2
singleton : Any -> Any -> Map
singleton key value = Map.empty.insert key value
## ALIAS dictionary, lookup table
GROUP Constants
## PRIVATE
ICON convert
Builds a map from two Vectors. The first vector contains the keys, and
the second vector contains the values. The two vectors must be of the
same length.
Arguments:
- keys: A vector of keys.
- values: A vector of values.
- error_on_duplicates: A flag which specifies if duplicate keys on the
input vector should result in an error. By default, set to `True`,
meaning that if two entries in the vector share the same key, an
`Illegal_Argument` error is raised. If set to `False`, the last entry
with a given key will be kept.
from_keys_and_values : Vector Any -> Vector Any -> Boolean -> Map ! Illegal_Argument
DEPRECATED Use Dictionary.from_keys_and_values instead.
from_keys_and_values : Vector Any -> Vector Any -> Boolean -> Any ! Deprecated
from_keys_and_values keys:Vector values:Vector error_on_duplicates:Boolean=True =
if keys.length != values.length then Error.throw (Illegal_Argument.Error "`Map.from_keys_and_values` encountered two vectors of different lengths.") else
keys.fold_with_index Map.empty current-> idx-> key->
if error_on_duplicates.not || (current.contains_key key . not) then current.insert key (values.at idx) else
Error.throw (Illegal_Argument.Error "`Map.from_keys_and_values` encountered duplicate key: "+key.to_display_text)
_ = [keys, values, error_on_duplicates]
Error.throw (Deprecated.Warning "Standard.Base.Data.Map.Map" "from_keys_and_values" "Deprecated: `Map.from_keys_and_values` has been replaced by `Dictionary.from_keys_and_values`.")
## ALIAS dictionary, lookup table
GROUP Constants
## PRIVATE
ICON convert
Builds a map from a vector of key-value pairs, with each key-value pair
represented as a 2 element vector.
Arguments:
- vec: A vector of key-value pairs (2 element vectors).
- error_on_duplicates: A flag which specifies if duplicate keys on the
input vector should result in an error. By default, set to `True`,
meaning that if two entries in the vector share the same key, an
`Illegal_Argument` error is raised. If set to `False`, the last entry
with a given key will be kept.
> Example
Building a map containing two key-value pairs.
import Standard.Base.Data.Map.Map
example_from_vector = Map.from_vector [["A", 1], ["B", 2]]
from_vector : Vector Any -> Boolean -> Map ! Illegal_Argument
DEPRECATED Use Dictionary.from_vector instead.
from_vector : Vector Any -> Boolean -> Any ! Deprecated
from_vector vec error_on_duplicates=True =
vec.fold Map.empty m-> el-> if el.length != 2 then Error.throw (Illegal_Argument.Error "`Map.from_vector` encountered an invalid value. Each value in the vector has to be a key-value pair - it must have exactly 2 elements.") else
key = el.at 0
value = el.at 1
if error_on_duplicates.not || (m.contains_key key . not) then m.insert key value else
Error.throw (Illegal_Argument.Error "`Map.from_vector` encountered duplicate key: "+key.to_display_text)
## GROUP Logical
ICON metadata
Returns True iff the Map is empty, i.e., does not have any entries.
is_empty : Boolean
is_empty self = self.size == 0
## GROUP Logical
ICON metadata
Returns True iff the Map is not empty, i.e., has at least one entry.
not_empty : Boolean
not_empty self = self.is_empty.not
## GROUP Metadata
ICON metadata
Returns the number of entries in this map.
size : Integer
size self = @Builtin_Method "Map.size"
## GROUP Metadata
ICON metadata
Returns the number of entries in this map.
length : Integer
length self = self.size
## GROUP Calculations
ICON row_add
Inserts a key-value mapping into this map, overriding any existing
instance of `key` with the new `value`.
Note that since the return type is also a `Map`, multiple `insert`
calls can be chained, e.g., `map.insert "A" 1 . insert "B" 2`.
Due to the limitation of the current implementation, inserts with a
key that is already contained in the map, or insert on a map instance that
is re-used in other computations, have a linear time complexity.
For all the other cases, the time complexity of this method is constant.
Arguments:
- key: The key to insert the value for.
- value: The value to associate with the `key`.
> Example
Insert the value "seven" into the map for the key 7.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_insert = Examples.map.insert 7 "seven"
insert : Any -> Any -> Map
insert self key value = @Builtin_Method "Map.insert"
## GROUP Selections
ICON table_clean
Removes an entry specified by the given key from this map, and
returns a new map without this entry. Throw `No_Such_Key.Error`
if `key` is not present.
Arguments:
- key: The key to look up in the map.
> Example
Remove key "A" from a map
import Standard.Data.Map.Map
Examples.map.remove "A"
remove : Any -> Map ! No_Such_Key
remove self key =
Panic.catch Any (self.remove_builtin key) _->
Error.throw (No_Such_Key.Error self key)
## GROUP Selections
ICON parse3
Gets the value associated with `key` in this map, or throws a
`No_Such_Key.Error` if `key` is not present.
This method has a constant time complexity.
Arguments:
- key: The key to look up in the map.
> Example
Looks up the value for the key "A" in a map.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_at = Examples.map.at "A"
at : Any -> Any ! No_Such_Key
at self key = self.get key (Error.throw (No_Such_Key.Error self key))
## ICON parse3
Gets the value associated with `key` in this map, or returns
`if_missing` if it isn't present.
This method has a constant time complexity.
Arguments:
- key: The key to look up in the map.
- if_missing: The value to use if the key isn't present.
> Example
Get the value for the key 2 in a map or instead return "zero" if it
isn't present.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_get = Examples.map.get 2 "zero"
get : Any -> Any -> Any
get self key ~if_missing=Nothing = self.get_builtin key if_missing
## GROUP Logical
ICON preparation
Returns True iff the Map contains the given `key`.
contains_key : Any -> Boolean
contains_key self key = @Builtin_Method "Map.contains_key"
## GROUP Selections
ICON select_column
Returns an unsorted vector of all the keys in this Map.
keys : Vector Any
keys self = self.to_vector.map pair-> pair.at 0
## GROUP Selections
ICON select_column
Returns an unsorted vector of all the values in this Map.
values : Vector Any
values self = self.to_vector.map pair-> pair.at 1
## ICON column_add
Maps a function over each value in this map.
Arguments:
- function: The function to apply to each value in the map, taking a
value and returning a value.
> Example
Append "_word" to all values in the map.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_map = Examples.map.map (+ "_word")
map : (Any -> Any) -> Map
map self function =
kv_func = _ -> function
self.map_with_key kv_func
## ICON column_add
Maps a function over each key-value pair in the map, transforming the
value.
Arguments:
- function: The function to apply to each key and value in the map,
taking a key and a value and returning a value.
> Example
Prepend the keys to the values in the map.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_map_with_key =
Examples.map.map_with_key (k -> v -> k.to_text + "-" + v)
map_with_key : (Any -> Any -> Any) -> Map
map_with_key self function =
Map.from_vector <| self.to_vector.map pair->
key = pair.first
value = pair.last
[key, (function key value)]
## ICON column_add
Maps a function over each key in this map.
Arguments:
- function: The function to apply to each key in the map, taking a key
and returning a key.
> Example
Doubling all keys in the map.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_map_keys = Examples.map.map_keys *2
map_keys : (Any -> Any) -> Map
map_keys self function =
trans_function = k -> v -> [function k, v]
self.transform trans_function
## ICON column_add
Transforms the map's keys and values to create a new map.
Arguments:
- function: The function used to transform the map, taking a key and a
value and returning a pair of `[key, value]`.
! Error Conditions
- If multiple map entries end up with duplicate keys after the
transformation, an `Illegal_Argument.Error` is thrown.
> Example
Turn all keys into `Text` and append "_word" to the values in the map.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_transform =
Examples.map.transform (k -> v -> [k.to_text, v + "_word"])
transform : (Any -> Any -> [Any, Any]) -> Map
transform self function =
func_pairs = p -> function (p.at 0) (p.at 1)
vec_transformed = self.to_vector.map func_pairs
new_map = Map.from_vector vec_transformed error_on_duplicates=True
new_map.catch Illegal_Argument error->
case error.message.starts_with "`Map.from_vector` encountered duplicate key" of
True ->
new_message = error.message.replace "from_vector" "transform"
Error.throw (Illegal_Argument.Error new_message error.cause)
False -> new_map
## ICON transform4
Combines the values in the map.
Arguments:
- init: The initial value for the fold.
- function: A binary function to apply to pairs of values in the map.
> Example
Find the length of the longest word in the map.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_fold = Examples.map.fold 0 (l -> r -> l.max r.length)
fold : Any -> (Any -> Any -> Any) -> Any
fold self init function = self.values.fold init function
## ICON transform4
Combines the key-value pairs in the map.
Arguments:
- init: The initial value for the fold.
- function: A function taking the left value, the current key, and the
current value, and combining them to yield a single value.
> Example
Glue the values in the map together with the keys.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_fold_with_key =
Examples.map.fold_with_key "" (l -> k -> v -> l + k.to_text + v)
fold_with_key : Any -> (Any -> Any -> Any -> Any) -> Any
fold_with_key self init function =
self.to_vector.fold init acc-> pair->
function acc pair.first pair.last
## PRIVATE
ADVANCED
Applies a function to each value in the map.
Arguments:
- function: The function to apply to each value in the map, taking a
value and returning anything.
This method does not return the results, so it is only useful for performing
computations with side-effects.
If the function returns a dataflow error, the error is converted to a
panic and thrown immediately stopping further processing.
> Example
Printing each value in the map.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_each = Examples.map.each IO.println
each : (Any -> Any) -> Nothing
each self function =
kv_func = _ -> function
self.each_with_key kv_func
## PRIVATE
ADVANCED
Applies a function to each key-value pair in the map.
Arguments:
- function: The function to apply to each key-value pair in the map,
taking a key and a value and returning anything.
This method does not return the results, so it is only useful for performing
computations with side-effects.
> Example
Printing each key and value in the map.
import Standard.Base.Data.Map.Map
import Standard.Examples
example_each_with_key = Examples.map.each_with_key k->v->
IO.println k
IO.println v
each_with_key : (Any -> Any -> Any) -> Nothing
each_with_key self function =
self.to_vector.each pair->
function pair.first pair.last
## GROUP Conversions
ICON convert
Returns an unsorted vector of key-value pairs (nested 2 element vectors).
`Map.from_vector` method is an inverse method, so the following expression
is true for all maps: `Map.from_vector map.to_vector == map`.
to_vector : Vector Any
to_vector self = @Builtin_Method "Map.to_vector"
## PRIVATE
Returns a text representation of this Map.
to_text : Text
to_text self = @Builtin_Method "Map.to_text"
## PRIVATE
get_builtin : Any -> Any -> Any
get_builtin self key ~if_missing = @Builtin_Method "Map.get_builtin"
_ = [vec, error_on_duplicates]
Error.throw (Deprecated.Warning "Standard.Base.Data.Map.Map" "from_vector" "Deprecated: `Map.from_vector` has been replaced by `Dictionary.from_vector`.")

View File

@ -1,8 +1,8 @@
import project.Any.Any
import project.Data.Array.Array
import project.Data.Dictionary.Dictionary
import project.Data.Filter_Condition.Filter_Condition
import project.Data.Json.JS_Object
import project.Data.Map.Map
import project.Data.Numbers.Integer
import project.Data.Range.Range
import project.Data.Text.Case_Sensitivity.Case_Sensitivity
@ -379,15 +379,16 @@ type Regex
Return a vector of all named group names.
named_groups : Vector Text
named_groups self =
map = polyglot_map_to_map self.internal_regex_object.groups
map.keys
dictionary = polyglot_map_to_dictionary self.internal_regex_object.groups
dictionary.keys
## ICON metadata
Return a map from group number to group name. Only includes named groups.
group_nums_to_names : Map Integer Text
Return a Dictionary from group number to group name. Only includes named
groups.
group_nums_to_names : Dictionary Integer Text
group_nums_to_names self =
map = polyglot_map_to_map self.internal_regex_object.groups
map.transform k-> v-> [v.at 0, k]
dictionary = polyglot_map_to_dictionary self.internal_regex_object.groups
dictionary.transform k-> v-> [v.at 0, k]
## ICON text
Escape the special characters in `expression` such that the result is a
@ -419,20 +420,20 @@ type Regex
Regex.compile self.pattern case_insensitive
## PRIVATE
Convert the polyglot map to a Map.
polyglot_map_to_map : Any -> Map Any Any
polyglot_map_to_map map =
Convert the polyglot map to a Dictionary.
polyglot_map_to_dictionary : Any -> Dictionary Any Any
polyglot_map_to_dictionary map =
polyglot_keys = Polyglot.get_members map
keys = Vector.from_polyglot_array polyglot_keys
pairs = keys.map key-> [key, Polyglot.get_member map key]
Map.from_vector pairs
Dictionary.from_vector pairs
## PRIVATE
Get the named group from the polyglot map.
read_group_map : Any -> Text -> Integer | Nothing
read_group_map polyglot_map name =
map = polyglot_map_to_map polyglot_map
map.get name
dictionary = polyglot_map_to_dictionary polyglot_map
dictionary.get name
## PRIVATE
match_to_group_maybe : Match | Nothing -> Text | Nothing

View File

@ -1,6 +1,6 @@
import project.Any.Any
import project.Data.Dictionary.Dictionary
import project.Data.Json.JS_Object
import project.Data.Map.Map
import project.Data.Numbers.Integer
import project.Data.Range.Range
import project.Data.Text.Regex.No_Such_Group
@ -260,7 +260,7 @@ type Match
## GROUP Metadata
ICON metadata
Gets a map containing the named capturing groups for the pattern,
Gets a Dictionary containing the named capturing groups for the pattern,
replacing the value for groups that did not participate in the match with
`default`.
@ -279,17 +279,18 @@ type Match
a named group that does not participate to the default value.
> Example
Get the map of all of the named groups in this match, replacing the
value for groups that didn't participate in the match with "UNMATCHED".
Get the Dictionary of all of the named groups in this match, replacing
the value for groups that didn't participate in the match with
"UNMATCHED".
pattern = Regex.compile "(.. .. )(?<letters>.+)()??(?<empty>)??"
input = "aa ab abc a bc bcd"
match = pattern.match input
## match.named_groups.keys.sort == ["empty", "letters"]
named_groups : Any -> Map Text (Text | Any)
named_groups : Any -> Dictionary Text (Text | Any)
named_groups self default=Nothing =
pattern_named_groups = self.pattern.named_groups
Map.from_vector <|
Dictionary.from_vector <|
pattern_named_groups.map name-> [name, self.text name default=default]
## ICON split

View File

@ -1,7 +1,7 @@
import project.Any.Any
import project.Data.Dictionary.Dictionary
import project.Data.Json.Extensions
import project.Data.Json.JS_Object
import project.Data.Map.Map
import project.Data.Numbers.Integer
import project.Data.Text.Encoding.Encoding
import project.Data.Text.Text
@ -305,9 +305,9 @@ type XML_Document
## GROUP Selections
ICON array_new
Gets a map containing of the attributes of an XML document.
attributes : Map Text Text ! XML_Error
attributes self = Map.empty
Gets a Dictionary containing of the attributes of an XML document.
attributes : Dictionary Text Text ! XML_Error
attributes self = Dictionary.empty
## GROUP Selections
ICON metadata
@ -508,19 +508,19 @@ type XML_Element
## GROUP Selections
ICON array_new
Gets a map containing of the attributes of an XML element.
Gets a Dictionary containing of the attributes of an XML element.
> Example
XML_Document.from_text '<foo bar="one">hello</foo>' . root_element . attributes
# => Map.from_vector [["bar", "one"]]
attributes : Map Text Text ! XML_Error
# => Dictionary.from_vector [["bar", "one"]]
attributes : Dictionary Text Text ! XML_Error
attributes self =
XML_Error.handle_java_exceptions <|
named_node_map = self.java_element.getAttributes
keys_and_values = 0.up_to named_node_map.getLength . map i->
node = named_node_map.item i
[node.getNodeName, node.getNodeValue]
Map.from_vector keys_and_values
Dictionary.from_vector keys_and_values
## GROUP Selections
ICON metadata

View File

@ -1,6 +1,6 @@
import project.Data.Base_64.Base_64
import project.Data.Dictionary.Dictionary
import project.Data.Json.JS_Object
import project.Data.Map.Map
import project.Data.Text.Text
import project.Data.Vector.Vector
import project.Enso_Cloud.Enso_File.Enso_Asset_Type
@ -56,7 +56,7 @@ type Enso_Secret
handle_already_exists _ =
message = "A secret with the name " + name.pretty + " already exists inside of directory " + parent_dir.name + "."
Error.throw (Illegal_Argument.Error message)
error_handlers = Map.from_vector [["resource_already_exists", handle_already_exists]]
error_handlers = Dictionary.from_vector [["resource_already_exists", handle_already_exists]]
id = Utils.http_request_as_json HTTP_Method.Post Utils.secrets_api body error_handlers=error_handlers
Enso_Secret.Value name id path

View File

@ -1,9 +1,9 @@
private
import project.Any.Any
import project.Data.Dictionary.Dictionary
import project.Data.Json.Invalid_JSON
import project.Data.Json.JS_Object
import project.Data.Map.Map
import project.Data.Text.Text
import project.Enso_Cloud.Enso_File.Enso_Asset_Type
import project.Enso_Cloud.Enso_File.Enso_File
@ -44,7 +44,7 @@ upload_file (local_file : File) (destination : Enso_File) (replace_existing : Bo
The `create_action` function is called with the existing asset for the parent
directory and for the file, if it already exists, or `Nothing` otherwise, and
with a mapping of error handlers that may be added to the request.
generic_create_asset (destination : Enso_File) (allow_existing : Boolean) (create_action : Existing_Enso_Asset -> (Existing_Enso_Asset | Nothing) -> Map -> Any) -> Any =
generic_create_asset (destination : Enso_File) (allow_existing : Boolean) (create_action : Existing_Enso_Asset -> (Existing_Enso_Asset | Nothing) -> Dictionary -> Any) -> Any =
parent_directory = destination.parent
if parent_directory.is_nothing then Error.throw (Illegal_Argument.Error "Please provide an asset name inside of the root directory.") else
parent_directory_asset = Existing_Enso_Asset.get_asset_reference_for parent_directory
@ -55,7 +55,7 @@ generic_create_asset (destination : Enso_File) (allow_existing : Boolean) (creat
File_Error.Not_Found _ -> Nothing
_ -> Error.throw error
if existing_asset.is_nothing.not && allow_existing.not then Error.throw (File_Error.Already_Exists destination) else
error_handlers = if existing_asset.is_nothing.not then Map.empty else
error_handlers = if existing_asset.is_nothing.not then Dictionary.empty else
## Currently we just report the race condition and request the user to re-run.
We don't retry automatically because it is harder than it seems - the `create_action` usually
depends on some user code that is writing to a stream (the callback given to `with_output_stream`).
@ -64,7 +64,7 @@ generic_create_asset (destination : Enso_File) (allow_existing : Boolean) (creat
into memory or a temporary file and relies on that for the retry.
For now, reporting the race condition in a sane way seemed like the simplest choice.
This situation should be very rare.
Map.from_vector [["resource_already_exists", Error.throw (Illegal_State.Error "A race-condition has been encountered - another process has created a colliding resource at "+destination.path+". Please try re-running the operation.")]]
Dictionary.from_vector [["resource_already_exists", Error.throw (Illegal_State.Error "A race-condition has been encountered - another process has created a colliding resource at "+destination.path+". Please try re-running the operation.")]]
create_action parent_directory_asset existing_asset error_handlers
## PRIVATE

View File

@ -1,7 +1,7 @@
private
import project.Data.Dictionary.Dictionary
import project.Data.Json.JS_Object
import project.Data.Map.Map
import project.Data.Text.Text
import project.Data.Time.Date_Time.Date_Time
import project.Data.Time.Date_Time_Formatter.Date_Time_Formatter
@ -83,7 +83,7 @@ type Existing_Enso_Asset
Resolves a path to an existing asset in the cloud.
resolve_path (path : Text) ~if_not_found = path.if_not_error <|
handle_not_found _ = Error.throw Not_Found
error_handlers = Map.from_vector [["resource_missing", handle_not_found]]
error_handlers = Dictionary.from_vector [["resource_missing", handle_not_found]]
uri = ((URI.from Utils.cloud_root_uri) / "path/resolve") . add_query_argument "path" path
response = Utils.http_request_as_json HTTP_Method.Get uri error_handlers=error_handlers

View File

@ -1,8 +1,8 @@
private
import project.Any.Any
import project.Data.Dictionary.Dictionary
import project.Data.Json.Invalid_JSON
import project.Data.Map.Map
import project.Data.Numbers.Integer
import project.Data.Text.Text
import project.Data.Time.Duration.Duration
@ -72,7 +72,7 @@ flush_caches = CloudAPI.flushCloudCaches
## PRIVATE
Performs a standard request to the Enso Cloud API,
parsing the result as JSON.
http_request_as_json (method : HTTP_Method) (url : URI) (body : Request_Body = Request_Body.Empty) (additional_headers : Vector = []) (error_handlers : Map Text (Any -> Any) = Map.empty) (retries : Integer = 3) -> Any ! Enso_Cloud_Error =
http_request_as_json (method : HTTP_Method) (url : URI) (body : Request_Body = Request_Body.Empty) (additional_headers : Vector = []) (error_handlers : Dictionary Text (Any -> Any) = Dictionary.empty) (retries : Integer = 3) -> Any ! Enso_Cloud_Error =
response = http_request method url body additional_headers error_handlers retries
response.decode_as_json.catch Invalid_JSON error->
Error.throw (Enso_Cloud_Error.Invalid_Response_Payload error)
@ -87,7 +87,7 @@ http_request_as_json (method : HTTP_Method) (url : URI) (body : Request_Body = R
Custom error handlers can be provided as a mapping from error codes
(defined in the cloud project) to functions that take the full JSON payload
and return a custom error.
http_request (method : HTTP_Method) (url : URI) (body : Request_Body = Request_Body.Empty) (additional_headers : Vector = []) (error_handlers : Map Text (Any -> Any) = Map.empty) (retries : Integer = 3) -> Response ! Enso_Cloud_Error = method.if_not_error <| url.if_not_error <| body.if_not_error <| additional_headers.if_not_error <|
http_request (method : HTTP_Method) (url : URI) (body : Request_Body = Request_Body.Empty) (additional_headers : Vector = []) (error_handlers : Dictionary Text (Any -> Any) = Dictionary.empty) (retries : Integer = 3) -> Response ! Enso_Cloud_Error = method.if_not_error <| url.if_not_error <| body.if_not_error <| additional_headers.if_not_error <|
all_headers = [authorization_header] + additional_headers
as_connection_error err = Error.throw (Enso_Cloud_Error.Connection_Error err)

View File

@ -3,9 +3,9 @@ private
import project.Any.Any
import project.Data.Array.Array
import project.Data.Array_Proxy.Array_Proxy
import project.Data.Dictionary.Dictionary
import project.Data.Index_Sub_Range.Index_Sub_Range
import project.Data.List.List
import project.Data.Map.Map
import project.Data.Maybe.Maybe
import project.Data.Numbers.Integer
import project.Data.Pair.Pair
@ -166,7 +166,7 @@ private on_problems_to_number on_problems:Problem_Behavior = case on_problems of
distinct vector on =
Vector.build builder->
vector.fold Map.empty existing->
vector.fold Dictionary.empty existing->
item->
key = on item
if (existing.get key False) then existing else
@ -174,7 +174,7 @@ distinct vector on =
existing.insert key True
duplicates vector on = Vector.build builder->
counts = vector.fold Map.empty current-> item->
counts = vector.fold Dictionary.empty current-> item->
key = on item
count = current.get key 0
current.insert key count+1

View File

@ -2,8 +2,10 @@ export project.Any.Any
export project.Data
export project.Data.Array.Array
export project.Data.Decimal.Decimal
export project.Data.Dictionary.Dictionary
export project.Data.Filter_Condition.Filter_Action
export project.Data.Filter_Condition.Filter_Condition
export project.Data.Hashset.Hashset
export project.Data.Index_Sub_Range.Index_Sub_Range
export project.Data.Interval.Bound
export project.Data.Interval.Interval
@ -25,7 +27,6 @@ export project.Data.Pair.Pair
export project.Data.Range.Range
export project.Data.Raw_Response
export project.Data.Regression
export project.Data.Set.Set
export project.Data.Sort_Direction.Sort_Direction
export project.Data.Statistics.Rank_Method
export project.Data.Statistics.Statistic

View File

@ -1,7 +1,7 @@
import project.Any.Any
import project.Data.Map.Map
import project.Data.Dictionary.Dictionary
import project.Data.Hashset.Hashset
import project.Data.Pair.Pair
import project.Data.Set.Set
import project.Data.Text.Encoding.Encoding
import project.Data.Text.Text
import project.Data.Time.Duration.Duration
@ -261,8 +261,8 @@ resolve_body_to_publisher_and_boundary body:Request_Body =
Build a BodyPublisher from the given form data.
The pair's second value is a content boundary in the case of a `multipart/form-data` form; otherwise, Nothing
build_form_body_publisher : Map Text (Text | File) -> Boolean -> Pair BodyPublisher Text
build_form_body_publisher (form_data:(Map Text (Text | File))) (url_encoded:Boolean=False) = case url_encoded of
build_form_body_publisher : Dictionary Text (Text | File) -> Boolean -> Pair BodyPublisher Text
build_form_body_publisher (form_data:(Dictionary Text (Text | File))) (url_encoded:Boolean=False) = case url_encoded of
True ->
body_builder = UrlencodedBodyBuilder.new
form_data.map_with_key key-> value->
@ -280,15 +280,15 @@ build_form_body_publisher (form_data:(Map Text (Text | File))) (url_encoded:Bool
Pair.new body_builder.build boundary
## PRIVATE
fetch_methods : Set HTTP_Method
fetch_methods = Set.from_vector [HTTP_Method.Get, HTTP_Method.Head, HTTP_Method.Options]
fetch_methods : Hashset HTTP_Method
fetch_methods = Hashset.from_vector [HTTP_Method.Get, HTTP_Method.Head, HTTP_Method.Options]
## PRIVATE
post_methods : Set HTTP_Method
post_methods = Set.from_vector [HTTP_Method.Post, HTTP_Method.Put, HTTP_Method.Patch, HTTP_Method.Delete]
post_methods : Hashset HTTP_Method
post_methods = Hashset.from_vector [HTTP_Method.Post, HTTP_Method.Put, HTTP_Method.Patch, HTTP_Method.Delete]
## PRIVATE
check_method : Set HTTP_Method -> Any -> Any -> Any ! Illegal_Argument
check_method : Hashset HTTP_Method -> Any -> Any -> Any ! Illegal_Argument
check_method allowed_methods method ~action =
if allowed_methods.contains method then action else
Error.throw (Illegal_Argument.Error ("Unsupported method " + method.to_display_text))

View File

@ -1,5 +1,5 @@
import project.Any.Any
import project.Data.Map.Map
import project.Data.Dictionary.Dictionary
import project.Data.Pair.Pair
import project.Data.Text.Text
import project.Data.Vector.Vector
@ -249,10 +249,10 @@ type Request
example_delete =
Request.delete (URI.parse "http://example.com") . with_form []
with_form : (Vector | Map) -> Request
with_form : (Vector | Dictionary) -> Request
with_form self parts =
form_data = case parts of
_ : Vector -> Map.from_vector parts
_ : Map -> parts
_ : Vector -> Dictionary.from_vector parts
_ : Dictionary -> parts
new_body = Request_Body.Form_Data form_data
Request.Value self.method self.uri self.headers new_body . with_headers [Header.application_x_www_form_urlencoded]

View File

@ -1,5 +1,5 @@
import project.Any.Any
import project.Data.Map.Map
import project.Data.Dictionary.Dictionary
import project.Data.Text.Encoding.Encoding
import project.Data.Text.Text
import project.Network.HTTP.Header.Header
@ -36,7 +36,7 @@ type Request_Body
- form_data: the form fields (text or file) to be sent
- url_encoded: if true, use a URL-encoded form; otherwise, use a
multi-part encoding.
Form_Data (form_data:(Map Text (Text | File))) (url_encoded:Boolean=False)
Form_Data (form_data:(Dictionary Text (Text | File))) (url_encoded:Boolean=False)
## Empty request body; used for GET
Empty

View File

@ -75,7 +75,7 @@ type Response
import Standard.Examples
example_headers = Map.from_vector error_on_duplicates=True (Examples.get_response.headers.map h-> [h.name, h.value])
example_headers = Dictionary.from_vector error_on_duplicates=True (Examples.get_response.headers.map h-> [h.name, h.value])
headers : Vector Header
headers self =
# This is a mapping that maps a header name to a list of values (since headers may be duplicated).

View File

@ -1,8 +1,6 @@
import project.Any.Any
import project.Data.Array.Array
import project.Data.Map.Map
import project.Data.Range.Range
import project.Data.Set.Set
import project.Data.Text.Text
import project.Data.Time.Date.Date
import project.Data.Time.Date_Range.Date_Range

View File

@ -1,9 +1,9 @@
import project.Any.Any
import project.Data.Array.Array
import project.Data.Hashset.Hashset
import project.Data.Maybe.Maybe
import project.Data.Numbers.Integer
import project.Data.Pair.Pair
import project.Data.Set.Set
import project.Data.Vector.Map_Error
import project.Data.Vector.No_Wrap
import project.Data.Vector.Vector

View File

@ -168,11 +168,11 @@ type Connection
Nothing -> Nothing
_ : Vector -> types
_ -> [types]
name_map = Map.from_vector [["TABLE_CAT", "Database"], ["TABLE_SCHEM", "Schema"], ["TABLE_NAME", "Name"], ["TABLE_TYPE", "Type"], ["REMARKS", "Description"], ["TYPE_CAT", "Type Database"], ["TYPE_SCHEM", "Type Schema"], ["TYPE_NAME", "Type Name"]]
name_dict = Dictionary.from_vector [["TABLE_CAT", "Database"], ["TABLE_SCHEM", "Schema"], ["TABLE_NAME", "Name"], ["TABLE_TYPE", "Type"], ["REMARKS", "Description"], ["TYPE_CAT", "Type Database"], ["TYPE_SCHEM", "Type Schema"], ["TYPE_NAME", "Type Name"]]
result = self.jdbc_connection.with_metadata metadata->
table = Managed_Resource.bracket (metadata.getTables database schema name_like types_vector) .close result_set->
result_set_to_table result_set self.dialect.get_type_mapping.make_column_fetcher
renamed = table.rename_columns name_map
renamed = table.rename_columns name_dict
if all_fields then renamed else
renamed.select_columns ["Database", "Schema", "Name", "Type", "Description"]
case include_hidden of

View File

@ -544,17 +544,17 @@ type DB_Table
> Example
Rename the "Alpha" column to "Beta"
table.rename_columns (Map.from_vector [["Alpha", "Beta"]])
table.rename_columns (Dictionary.from_vector [["Alpha", "Beta"]])
> Example
Rename the last column to "LastColumn"
table.rename_columns (Map.from_vector [[-1, "LastColumn"]])
table.rename_columns (Dictionary.from_vector [[-1, "LastColumn"]])
> Example
Rename the "Alpha" column to "Beta" and last column to "LastColumn"
table.rename_columns (Map.from_vector [["Alpha", "Beta"], [-1, "LastColumn"]])
table.rename_columns (Dictionary.from_vector [["Alpha", "Beta"], [-1, "LastColumn"]])
> Example
Rename the first column to "FirstColumn"
@ -569,12 +569,12 @@ type DB_Table
> Example
For all columns starting with the prefix `name=`, replace it with `key:`.
table.rename_columns (Map.from_vector [["name=(.*)".to_regex, "key:$1"]])
table.rename_columns (Dictionary.from_vector [["name=(.*)".to_regex, "key:$1"]])
@column_map Widget_Helpers.make_rename_name_vector_selector
rename_columns : Map (Text | Integer | Regex) Text | Vector Text | Vector Vector -> Case_Sensitivity -> Boolean -> Problem_Behavior -> DB_Table ! Missing_Input_Columns | Ambiguous_Column_Rename | Too_Many_Column_Names_Provided | Invalid_Column_Names | Duplicate_Output_Column_Names
rename_columns self (column_map:(Table | Map | Vector)=["Column"]) (case_sensitivity:Case_Sensitivity=..Default) (error_on_missing_columns:Boolean=True) (on_problems:Problem_Behavior=..Report_Warning) = case column_map of
rename_columns : Table | Dictionary (Text | Integer | Regex) Text | Vector Text | Vector Vector -> Case_Sensitivity -> Boolean -> Problem_Behavior -> DB_Table ! Missing_Input_Columns | Ambiguous_Column_Rename | Too_Many_Column_Names_Provided | Invalid_Column_Names | Duplicate_Output_Column_Names
rename_columns self (column_map:(Table | Dictionary | Vector)=["Column"]) (case_sensitivity:Case_Sensitivity=..Default) (error_on_missing_columns:Boolean=True) (on_problems:Problem_Behavior=..Report_Warning) = case column_map of
_ : Table ->
resolved = Table_Helpers.read_name_map_from_table column_map
resolved = Table_Helpers.read_name_mapping_from_table column_map
self.rename_columns resolved case_sensitivity error_on_missing_columns on_problems
_ ->
new_names = Table_Helpers.rename_columns self.column_naming_helper self.internal_columns column_map case_sensitivity error_on_missing_columns on_problems
@ -1035,26 +1035,26 @@ type DB_Table
Warning.set result []
## PRIVATE
A helper that creates a two-column table from a Map.
A helper that creates a two-column table from a Dictionary.
The keys of the `Map` become the first column, with name
`key_column_name`, and the values of the `Map` become the second column,
with name `value_column_name`.
The keys of the `Dictionary` become the first column, with name
`key_column_name`, and the values become the second column, with name
`value_column_name`.
For the in-memory database, the `Map` can be empty. For the database
backends, it must not be empty.
For the in-memory database, the `Dictionary` can be empty. For the
database backends, it must not be empty.
Arguments:
- map: The `Map` to create the table from.
- dict: The `Dictionary` to create the table from.
- key_column_name: The name to use for the first column.
- value_column_name: The name to use for the second column.
make_table_from_map : Map Any Any -> Text -> Text -> Table
make_table_from_map self map key_column_name value_column_name =
total_size = map.size * 2
make_table_from_dictionary : Dictionary Any Any -> Text -> Text -> Table
make_table_from_dictionary self dict key_column_name value_column_name =
total_size = dict.size * 2
if map.is_empty then Error.throw (Illegal_Argument.Error "Map argument cannot be empty") else
if total_size > MAX_LITERAL_ELEMENT_COUNT then Error.throw (Illegal_Argument.Error "Map argument is too large ("+map.size.to_text+" entries): materialize a table into the database instead") else
keys_and_values = map.to_vector
if dict.is_empty then Error.throw (Illegal_Argument.Error "Dictionary cannot be empty") else
if total_size > MAX_LITERAL_ELEMENT_COUNT then Error.throw (Illegal_Argument.Error "Dictionary is too large ("+dict.size.to_text+" entries): materialize a table into the database instead") else
keys_and_values = dict.to_vector
self.make_table_from_vectors [keys_and_values.map .first, keys_and_values.map .second] [key_column_name, value_column_name]
## PRIVATE
@ -1683,8 +1683,8 @@ type DB_Table
@columns (Widget_Helpers.make_column_name_multi_selector add_regex=True add_by_type=True)
@from_column Widget.Text_Input
@to_column Widget.Text_Input
replace : (DB_Table | Map) -> Vector (Integer | Text | Regex | By_Type) | Text | Integer | Regex | By_Type -> (Text | Integer | Nothing) -> (Text | Integer | Nothing) -> Boolean -> Problem_Behavior -> DB_Table ! Missing_Input_Columns | Non_Unique_Key | Unmatched_Rows_In_Lookup
replace self lookup_table:(DB_Table | Map) columns:(Vector (Integer | Text | Regex | By_Type) | Text | Integer | Regex | By_Type) from_column:(Text | Integer | Nothing)=Nothing to_column:(Text | Integer | Nothing)=Nothing allow_unmatched_rows:Boolean=True on_problems:Problem_Behavior=..Report_Warning =
replace : (DB_Table | Dictionary) -> Vector (Integer | Text | Regex | By_Type) | Text | Integer | Regex | By_Type -> (Text | Integer | Nothing) -> (Text | Integer | Nothing) -> Boolean -> Problem_Behavior -> DB_Table ! Missing_Input_Columns | Non_Unique_Key | Unmatched_Rows_In_Lookup
replace self lookup_table:(DB_Table | Dictionary) columns:(Vector (Integer | Text | Regex | By_Type) | Text | Integer | Regex | By_Type) from_column:(Text | Integer | Nothing)=Nothing to_column:(Text | Integer | Nothing)=Nothing allow_unmatched_rows:Boolean=True on_problems:Problem_Behavior=..Report_Warning =
Replace_Helpers.replace self lookup_table columns from_column to_column allow_unmatched_rows on_problems
## ALIAS join by row position

View File

@ -17,33 +17,32 @@ from project.Internal.IR.Operation_Metadata import Row_Number_Metadata
type Dialect_Operations
## PRIVATE
Operations supported by a particular SQL dialect and how they are implemeneted.
Operations supported by a particular SQL dialect and how they are
implemented.
Arguments:
- operation_map: The mapping which maps operation names to their
- operations_dict: Dictionary mapping operation names to their
implementations; each implementation is a function which takes SQL
builders for the arguments, and optionally an additional metadata
argument, and should return a SQL builder yielding code for the whole
operation.
Value (operation_map:(Map Text (Vector (SQL_Builder->SQL_Builder))))
Value (operations_dict:(Dictionary Text (Vector (SQL_Builder->SQL_Builder))))
## PRIVATE
Creates a copy of the dialect that supports additional operations or
overrides existing ones.
# extend_with : Vector [Text, Vector SQL_Builder -> SQL_Builder] -> Dialect_Operations
extend_with : Vector Any -> Dialect_Operations
extend_with self mappings =
new_map = mappings.fold self.operation_map (m -> el -> m.insert (el.at 0) (el.at 1))
Dialect_Operations.Value new_map
new_dict = mappings.fold self.operations_dict (m -> el -> m.insert (el.at 0) (el.at 1))
Dialect_Operations.Value new_dict
## PRIVATE
Checks if an operation is supported by the dialect.
is_supported : Text -> Boolean
is_supported self operation =
self.operation_map.contains_key operation
self.operations_dict.contains_key operation
## PRIVATE
@ -200,8 +199,8 @@ base_dialect_operations =
contains = [["IS_IN", make_is_in], ["IS_IN_COLUMN", make_is_in_column]]
types = [simple_cast]
windows = [["ROW_NUMBER", make_row_number], ["ROW_NUMBER_IN_GROUP", make_row_number_in_group]]
base_map = Map.from_vector (arith + logic + compare + functions + agg + counts + text + nulls + contains + types + windows)
Dialect_Operations.Value base_map
base_dict = Dictionary.from_vector (arith + logic + compare + functions + agg + counts + text + nulls + contains + types + windows)
Dialect_Operations.Value base_dict
## PRIVATE
is_empty = lift_unary_op "IS_EMPTY" arg->
@ -311,7 +310,7 @@ generate_expression dialect expr = case expr of
escaped = value.replace "'" "''"
SQL_Builder.code ("'" + escaped + "'")
SQL_Expression.Operation kind arguments metadata ->
op = dialect.dialect_operations.operation_map.get kind (Error.throw <| Unsupported_Database_Operation.Error kind)
op = dialect.dialect_operations.operations_dict.get kind (Error.throw <| Unsupported_Database_Operation.Error kind)
parsed_args = arguments.map (generate_expression dialect)
result = op parsed_args
# If the function expects more arguments, we pass the metadata as the last argument.

View File

@ -70,11 +70,10 @@ type Join_Subquery_Setup
## PRIVATE
Creates a mapping from names of columns in the original table to
corresponding columns in the created subquery.
column_mapping : Map Text Internal_Column
column_mapping : Dictionary Text Internal_Column
column_mapping self =
Map.from_vector <|
self.old_columns.zip self.new_columns old-> new->
[old.name, new]
Dictionary.from_vector <|
self.old_columns.zip self.new_columns old->new->[old.name, new]
## PRIVATE
prepare_subqueries : Connection -> DB_Table -> DB_Table -> Boolean -> Boolean -> Pair Join_Subquery_Setup

View File

@ -116,7 +116,7 @@ type Postgres_Dialect
inner_table_alias = table_name_deduplicator.make_unique table.name+"_inner"
setup = table.context.as_subquery inner_table_alias [table.internal_columns]
new_columns = setup.new_columns.first
column_mapping = Map.from_vector <| new_columns.map c-> [c.name, c]
column_mapping = Dictionary.from_vector <| new_columns.map c-> [c.name, c]
new_key_columns = key_columns.map c-> column_mapping.at c.name
type_mapping = self.get_type_mapping
distinct_expressions = new_key_columns.map column->
@ -563,14 +563,14 @@ decimal_mod = Base_Generator.lift_binary_op "DECIMAL_MOD" x-> y->
x ++ " - FLOOR(CAST(" ++ x ++ " AS decimal) / CAST(" ++ y ++ " AS decimal)) * " ++ y
## PRIVATE
supported_replace_params : Set Replace_Params
supported_replace_params : Hashset Replace_Params
supported_replace_params =
e0 = [Replace_Params.Value Text Case_Sensitivity.Default False, Replace_Params.Value Text Case_Sensitivity.Default True, Replace_Params.Value Text Case_Sensitivity.Sensitive False]
e1 = [Replace_Params.Value Text Case_Sensitivity.Sensitive True, Replace_Params.Value Text Case_Sensitivity.Insensitive False, Replace_Params.Value Text Case_Sensitivity.Insensitive True]
e2 = [Replace_Params.Value Regex Case_Sensitivity.Default False, Replace_Params.Value Regex Case_Sensitivity.Default True, Replace_Params.Value Regex Case_Sensitivity.Sensitive False]
e3 = [Replace_Params.Value Regex Case_Sensitivity.Sensitive True, Replace_Params.Value Regex Case_Sensitivity.Insensitive False, Replace_Params.Value Regex Case_Sensitivity.Insensitive True]
e4 = [Replace_Params.Value DB_Column Case_Sensitivity.Default False, Replace_Params.Value DB_Column Case_Sensitivity.Sensitive False]
Set.from_vector <| e0 + e1 + e2 + e3 + e4
Hashset.from_vector <| e0 + e1 + e2 + e3 + e4
## PRIVATE
replace : Vector SQL_Builder -> Any -> SQL_Builder

View File

@ -126,14 +126,14 @@ type Postgres_Type_Mapping
Column_Fetcher_Module.default_fetcher_for_value_type value_type
## PRIVATE
simple_types_map = Map.from_vector <|
simple_types_map = Dictionary.from_vector <|
ints = [[Types.SMALLINT, Value_Type.Integer Bits.Bits_16], [Types.BIGINT, Value_Type.Integer Bits.Bits_64], [Types.INTEGER, Value_Type.Integer Bits.Bits_32]]
floats = [[Types.DOUBLE, Value_Type.Float Bits.Bits_64], [Types.REAL, Value_Type.Float Bits.Bits_32]]
other = [[Types.DATE, Value_Type.Date], [Types.TIME, Value_Type.Time]]
ints + floats + other
## PRIVATE
complex_types_map = Map.from_vector <|
complex_types_map = Dictionary.from_vector <|
make_decimal sql_type =
Value_Type.Decimal sql_type.precision sql_type.scale
make_varchar sql_type =

View File

@ -125,7 +125,7 @@ type SQLite_Dialect
inner_table_alias = table_name_deduplicator.make_unique table.name+"_inner"
setup = table.context.as_subquery inner_table_alias [table.internal_columns]
new_columns = setup.new_columns.first
column_mapping = Map.from_vector <| new_columns.map c-> [c.name, c]
column_mapping = Dictionary.from_vector <| new_columns.map c-> [c.name, c]
new_key_columns = key_columns.map c-> column_mapping.at c.name
type_mapping = self.get_type_mapping
distinct_expressions = new_key_columns.map column->
@ -447,10 +447,10 @@ mod_op = Base_Generator.lift_binary_op "MOD" x-> y->
x ++ " - FLOOR(CAST(" ++ x ++ " AS REAL) / CAST(" ++ y ++ " AS REAL)) * " ++ y
## PRIVATE
supported_replace_params : Set Replace_Params
supported_replace_params : Hashset Replace_Params
supported_replace_params =
e = [Replace_Params.Value Text Case_Sensitivity.Default False, Replace_Params.Value Text Case_Sensitivity.Sensitive False, Replace_Params.Value Text Case_Sensitivity.Default True, Replace_Params.Value Text Case_Sensitivity.Sensitive True, Replace_Params.Value Text Case_Sensitivity.Insensitive True]
Set.from_vector e
Hashset.from_vector e
## PRIVATE
replace : Vector SQL_Builder -> Any -> SQL_Builder

View File

@ -96,7 +96,7 @@ type SQLite_Type_Mapping
infer_return_type : (SQL_Expression -> SQL_Type_Reference) -> Text -> Vector -> SQL_Expression -> SQL_Type_Reference
infer_return_type infer_from_database_callback op_name arguments expression =
_ = [infer_from_database_callback, expression]
handler = operations_map.get op_name (_ -> Error.throw (Illegal_State.Error "Impossible: Unknown operation "+op_name+". This is a bug in the Database library."))
handler = operations_dict.get op_name (_ -> Error.throw (Illegal_State.Error "Impossible: Unknown operation "+op_name+". This is a bug in the Database library."))
sql_type = handler arguments
SQL_Type_Reference.from_constant sql_type
@ -127,7 +127,7 @@ type SQLite_Type_Mapping
For types like dates - we map them to unsupported type, because date
operations in SQLite are currently not supported due to their weird storage.
simple_types_map = Map.from_vector <|
simple_types_map = Dictionary.from_vector <|
ints = [Types.TINYINT, Types.SMALLINT, Types.BIGINT, Types.INTEGER] . map x-> [x, default_integer]
floats = [Types.DOUBLE, Types.REAL, Types.FLOAT] . map x-> [x, default_float]
# We treat numeric as a float, since that is what really sits in SQLite under the hood.
@ -142,13 +142,13 @@ simple_types_map = Map.from_vector <|
https://www.sqlite.org/datatype3.html#affinity_name_examples
However, with this the date-time columns will be mapped to the numeric type.
Instead, we want to treat such columns as Text, so we override the mapping.
name_based_workarounds = Map.from_vector <|
name_based_workarounds = Dictionary.from_vector <|
["TIME", "DATE", "DATETIME", "TIMESTAMP"] . map x-> [x, default_text]
## PRIVATE
Maps operation names to functions that infer its result type.
operations_map : Map Text (Vector -> SQL_Type)
operations_map =
operations_dict : Dictionary Text (Vector -> SQL_Type)
operations_dict =
find_type arg = case arg of
column : DB_Column -> column.value_type
internal_column : Internal_Column ->
@ -198,7 +198,7 @@ operations_map =
arithmetic_ops = ["ADD_NUMBER", "-", "*", "^", "%", "SUM"]
merge_input_types_ops = ["ROW_MAX", "ROW_MIN", "MAX", "MIN", "FILL_NULL", "COALESCE"]
others = [["IIF", handle_iif], ["CAST", handle_cast], ["CASE", handle_case], ["RUNTIME_ERROR", handle_runtime_error]]
Map.from_vector <|
Dictionary.from_vector <|
v1 = always_boolean_ops.map [_, const SQLite_Types.boolean]
v2 = always_floating_ops.map [_, const SQLite_Types.real]
v3 = always_integer_ops.map [_, const SQLite_Types.integer]

View File

@ -190,8 +190,8 @@ resolve_primary_key structure primary_key = case primary_key of
if key.is_a Text then key else
Error.throw (Illegal_Argument.Error ("Primary key must be a vector of column names, instead got a " + (Meta.type_of key . to_display_text)))
validated.if_not_error <|
column_names = Set.from_vector (structure.map .name)
missing_columns = (Set.from_vector primary_key).difference column_names
column_names = Hashset.from_vector (structure.map .name)
missing_columns = (Hashset.from_vector primary_key).difference column_names
if missing_columns.not_empty then Error.throw (Missing_Input_Columns.Error missing_columns.to_vector) else
primary_key
@ -470,13 +470,13 @@ check_update_arguments_structure_match source_table target_table key_columns upd
if source_type.can_be_widened_to target_type then [Inexact_Type_Coercion.Warning source_type target_type unavailable=False] else
Error.throw (Column_Type_Mismatch.Error source_column.name target_type source_type)
source_columns = Set.from_vector source_table.column_names
target_columns = Set.from_vector target_table.column_names
source_columns = Hashset.from_vector source_table.column_names
target_columns = Hashset.from_vector target_table.column_names
extra_columns = source_columns.difference target_columns
if extra_columns.not_empty then Error.throw (Unmatched_Columns.Error extra_columns.to_vector) else
missing_columns = target_columns.difference source_columns
if missing_columns.not_empty && error_on_missing_columns then Error.throw (Missing_Input_Columns.Error missing_columns.to_vector "the source table") else
key_set = Set.from_vector key_columns
key_set = Hashset.from_vector key_columns
missing_source_key_columns = key_set.difference source_columns
missing_target_key_columns = key_set.difference target_columns
if missing_source_key_columns.not_empty then Error.throw (Missing_Input_Columns.Error missing_source_key_columns.to_vector "the source table") else
@ -600,10 +600,10 @@ type Delete_Rows_Source
check_delete_rows_arguments target_table key_values_to_delete key_columns ~continuation =
check_target_table_for_update target_table <|
if key_columns.is_empty then Error.throw (Illegal_Argument.Error "One or more key columns must be provided to correlate the rows to be deleted.") else
key_set = Set.from_vector key_columns
missing_target_key_columns = key_set . difference (Set.from_vector target_table.column_names)
key_set = Hashset.from_vector key_columns
missing_target_key_columns = key_set . difference (Hashset.from_vector target_table.column_names)
if missing_target_key_columns.not_empty then Error.throw (Missing_Input_Columns.Error missing_target_key_columns.to_vector "the target table") else
missing_source_key_columns = key_set . difference (Set.from_vector key_values_to_delete.column_names)
missing_source_key_columns = key_set . difference (Hashset.from_vector key_values_to_delete.column_names)
if missing_source_key_columns.not_empty then Error.throw (Missing_Input_Columns.Error missing_source_key_columns.to_vector "the key values to delete table") else
continuation

View File

@ -112,9 +112,9 @@ json_object = json.first
list : List
list = List.Cons 1 (List.Cons 2 (List.Cons 3 List.Nil))
## A simple map that contains some numbers mapped to their word equivalents.
map : Map
map = Map.empty . insert 1 "one" . insert 3 "three" . insert 5 "five"
## A simple dictionary that contains some numbers mapped to their word equivalents.
dictionary : Dictionary
dictionary = Dictionary.empty . insert 1 "one" . insert 3 "three" . insert 5 "five"
## A dummy type that is used for example purposes.
type No_Methods

View File

@ -119,7 +119,7 @@ type Snowflake_Dialect
inner_table_alias = table_name_deduplicator.make_unique table.name+"_inner"
setup = (Internals_Access.get_context table).as_subquery inner_table_alias [Internals_Access.internal_columns table]
new_columns = setup.new_columns.first
column_mapping = Map.from_vector <| new_columns.map c-> [c.name, c]
column_mapping = Dictionary.from_vector <| new_columns.map c-> [c.name, c]
new_key_columns = key_columns.map c-> column_mapping.at c.name
type_mapping = self.get_type_mapping
distinct_expressions = new_key_columns.map column->
@ -464,14 +464,14 @@ decimal_mod = Base_Generator.lift_binary_op "DECIMAL_MOD" x-> y->
x ++ " - FLOOR(CAST(" ++ x ++ " AS decimal) / CAST(" ++ y ++ " AS decimal)) * " ++ y
## PRIVATE
supported_replace_params : Set Replace_Params
supported_replace_params : Hashset Replace_Params
supported_replace_params =
e0 = [Replace_Params.Value Text Case_Sensitivity.Default False, Replace_Params.Value Text Case_Sensitivity.Default True, Replace_Params.Value Text Case_Sensitivity.Sensitive False]
e1 = [Replace_Params.Value Text Case_Sensitivity.Sensitive True, Replace_Params.Value Text Case_Sensitivity.Insensitive False, Replace_Params.Value Text Case_Sensitivity.Insensitive True]
e2 = [Replace_Params.Value Regex Case_Sensitivity.Default False, Replace_Params.Value Regex Case_Sensitivity.Default True, Replace_Params.Value Regex Case_Sensitivity.Sensitive False]
e3 = [Replace_Params.Value Regex Case_Sensitivity.Sensitive True, Replace_Params.Value Regex Case_Sensitivity.Insensitive False, Replace_Params.Value Regex Case_Sensitivity.Insensitive True]
e4 = [Replace_Params.Value DB_Column Case_Sensitivity.Default False, Replace_Params.Value DB_Column Case_Sensitivity.Sensitive False]
Set.from_vector <| e0 + e1 + e2 + e3 + e4
Hashset.from_vector <| e0 + e1 + e2 + e3 + e4
## PRIVATE
replace : Vector SQL_Builder -> Any -> SQL_Builder

View File

@ -118,14 +118,14 @@ type Snowflake_Type_Mapping
_ -> Column_Fetcher_Module.default_fetcher_for_value_type value_type
## PRIVATE
simple_types_map = Map.from_vector <|
simple_types_map = Dictionary.from_vector <|
ints = [[Types.TINYINT, Value_Type.Byte], [Types.SMALLINT, Value_Type.Integer Bits.Bits_16], [Types.BIGINT, Value_Type.Integer Bits.Bits_64], [Types.INTEGER, Value_Type.Integer Bits.Bits_32]]
floats = [[Types.DOUBLE, Value_Type.Float Bits.Bits_64], [Types.REAL, Value_Type.Float Bits.Bits_32]]
other = [[Types.DATE, Value_Type.Date], [Types.TIME, Value_Type.Time], [Types.BOOLEAN, Value_Type.Boolean]]
ints + floats + other
## PRIVATE
complex_types_map = Map.from_vector <|
complex_types_map = Dictionary.from_vector <|
make_decimal sql_type =
Value_Type.Decimal sql_type.precision sql_type.scale
make_varchar sql_type =

View File

@ -1716,7 +1716,7 @@ type Column
True ->
run_vectorized_binary_op self op_name as_vector expected_result_type=Value_Type.Boolean skip_nulls=False new_name=result_name
False ->
set = Set.from_vector as_vector error_on_duplicates=False
set = Hashset.from_vector as_vector error_on_duplicates=False
apply_unary_map self result_name set.contains_relational Value_Type.Boolean nothing_unchanged=False
## GROUP Standard.Base.Conversions

View File

@ -20,12 +20,12 @@ Convertible_To_Columns.from (that:JS_Object) =
Convertible_To_Columns.Value that.field_names (that.get _)
## PRIVATE
Convertible_To_Columns.from (that:Map) =
Convertible_To_Columns.from (that:Dictionary) =
pairs = that.keys.map k-> [k.to_text, k]
field_map = pairs.if_not_error <|
Map.from_vector pairs error_on_duplicates=True . catch Illegal_Argument _->
field_dict = pairs.if_not_error <|
Dictionary.from_vector pairs error_on_duplicates=True . catch Illegal_Argument _->
Error.throw (Illegal_Argument.Error "Cannot convert "+that.to_display_text+" to a set of columns, because its keys are duplicated when converted to text.")
Convertible_To_Columns.Value field_map.keys (k-> that.get (field_map.get k))
Convertible_To_Columns.Value field_dict.keys (k-> that.get (field_dict.get k))
## PRIVATE
Convertible_To_Columns.from (that:Pair) =
@ -67,5 +67,5 @@ Convertible_To_Columns.from (that:XML_Element) =
has_child_nodes = that_children.any (_.is_a XML_Element)
children = if that_children.is_empty.not && has_child_nodes then [["Children", that_children]] else []
value = if that_children.is_empty.not && has_child_nodes.not then [["Value", that.text]] else []
as_map = Map.from_vector (name + tags + children + value)
Convertible_To_Columns.from as_map
as_dict = Dictionary.from_vector (name + tags + children + value)
Convertible_To_Columns.from as_dict

View File

@ -54,7 +54,7 @@ Convertible_To_Rows.from that:Pair = Convertible_To_Rows.Value that.length (that
Convertible_To_Rows.from that:Date_Range = Convertible_To_Rows.Value that.length (that.get _)
## PRIVATE
Convertible_To_Rows.from that:Map =
Convertible_To_Rows.from that:Dictionary =
vals = that.to_vector.map p-> Key_Value.Pair p.first p.second
Convertible_To_Rows.Value vals.length (vals.get _) ["Key", "Value"]

View File

@ -755,7 +755,7 @@ type Truncated_Column_Names
## PRIVATE
Indicates that some column names were truncated to fit the maximum length
supported by the backend.
Warning (names_map : Map Text Text)
Warning (names_map : Dictionary Text Text)
## PRIVATE
Get the original column names.

View File

@ -97,47 +97,46 @@ create_table_from_objects (value : Convertible_To_Rows) (fields : Vector | Nothi
Java_Problems.with_problem_aggregator Problem_Behavior.Report_Warning java_problem_aggregator->
preset_fields = fields.is_nothing.not
initial_map = if preset_fields.not then Map.empty else
Map.from_vector (fields.map field_name-> [field_name, Java_Exports.make_inferred_builder len java_problem_aggregator]) error_on_duplicates=False
initial_dict = if preset_fields.not then Dictionary.empty else
Dictionary.from_vector (fields.map field_name-> [field_name, Java_Exports.make_inferred_builder len java_problem_aggregator]) error_on_duplicates=False
# This is used to ensure that field names in the resulting table are in the order they were encountered.
discovered_field_names = Builder.new
builder_map = case preset_fields of
builder_dict = case preset_fields of
# Just get the queried keys from each object.
True ->
0.up_to len . each idx->
v = (value.getter idx).to Convertible_To_Columns
initial_map.each_with_key field_name-> builder->
initial_dict.each_with_key field_name-> builder->
builder.append (v.getter field_name)
initial_map
initial_dict
# In this mode we are discovering the key set as we go.
False ->
0.up_to len . fold initial_map current_map-> idx->
0.up_to len . fold initial_dict current_dict-> idx->
v = (value.getter idx).to Convertible_To_Columns
v.field_names.fold current_map inner_current_map-> f->
existing_builder = inner_current_map.get f Nothing
v.field_names.fold current_dict inner_current_dict-> f->
existing_builder = inner_current_dict.get f Nothing
builder = existing_builder.if_nothing <|
discovered_field_names.append f
Java_Exports.make_inferred_builder len java_problem_aggregator
builder.fillUpToSize idx
builder.append (v.getter f)
new_map = if existing_builder.is_nothing.not then inner_current_map else
inner_current_map.insert f builder
new_map
if existing_builder.is_nothing.not then inner_current_dict else
inner_current_dict.insert f builder
# Seal all builders and create columns
column_map = builder_map.map_with_key name-> builder->
column_dict = builder_dict.map_with_key name-> builder->
builder.fillUpToSize len
Column.from_storage name builder.seal
column_map.if_not_error <|
column_dict.if_not_error <|
# Create a vector of columns, preserving the original order if it was specified.
columns = case preset_fields of
True -> fields.distinct.map column_map.get
True -> fields.distinct.map column_dict.get
False ->
if discovered_field_names.is_empty then Error.throw (Illegal_Argument.Error "Unable to generate column names as all inputs had no fields.") else
discovered_field_names.to_vector.map column_map.get
discovered_field_names.to_vector.map column_dict.get
Table.new columns

View File

@ -172,7 +172,7 @@ fan_out_to_rows_and_columns_fixed input_storage function at_least_one_row:Boolea
fan_out_to_rows_and_columns_dynamic : Any -> (Any -> Vector (Vector Any)) -> Boolean -> (Any -> Text) -> (Integer -> Any) -> Problem_Builder -> Vector
fan_out_to_rows_and_columns_dynamic input_storage function at_least_one_row column_names_for_row column_builder problem_builder =
# Accumulates the outputs of the function.
column_map = Ref.new Map.empty
column_dict = Ref.new Dictionary.empty
output_column_builders = Builder.new
# Guess that most of the time, we'll get at least one value for each input.
@ -180,7 +180,7 @@ fan_out_to_rows_and_columns_dynamic input_storage function at_least_one_row colu
# Column Builder add function
add_column n current_length =
column_map.put (column_map.get.insert n output_column_builders.length)
column_dict.put (column_dict.get.insert n output_column_builders.length)
builder = column_builder num_input_rows
builder.appendNulls current_length
output_column_builders.append builder
@ -200,11 +200,11 @@ fan_out_to_rows_and_columns_dynamic input_storage function at_least_one_row colu
# Add any missing columns.
row_column_names.each n->
if column_map.get.contains_key n . not then
if column_dict.get.contains_key n . not then
add_column n order_mask_positions.length
# Append each group of values to the builder.
current_columns = column_map.get
current_columns = column_dict.get
output_values.each row_unchecked->
row = uniform_length row_column_names.length row_unchecked problem_builder
row_column_names.each_with_index i->n->
@ -220,7 +220,7 @@ fan_out_to_rows_and_columns_dynamic input_storage function at_least_one_row colu
repeat_each output_values.length <| order_mask_positions.append i
# Build the output column
output_columns = column_map.get.to_vector.sort on=_.second . map pair->
output_columns = column_dict.get.to_vector.sort on=_.second . map pair->
Column.from_storage pair.first (output_column_builders.at pair.second . seal)
[output_columns, order_mask_positions]

View File

@ -50,7 +50,7 @@ prepare_columns_for_lookup base_table lookup_table key_columns_selector add_new_
problems_to_add = Builder.new
key_columns.if_not_error <| lookup_table_key_columns.if_not_error <|
key_set = Set.from_vector key_columns
key_set = Hashset.from_vector key_columns
my_updated_columns = base_table.columns.map on_problems=No_Wrap base_column->
base_column_name = base_column.name
is_key = key_set.contains base_column_name
@ -67,7 +67,7 @@ prepare_columns_for_lookup base_table lookup_table key_columns_selector add_new_
Nothing -> Lookup_Column.Keep_Column base_column
_ -> merge_columns base_column lookup_column allow_unmatched_rows
my_column_set = Set.from_vector base_table.column_names
my_column_set = Hashset.from_vector base_table.column_names
extra_columns = lookup_table.columns.filter col->
is_new = my_column_set.contains col.name . not
is_new

View File

@ -10,7 +10,7 @@ import project.Value_Type.By_Type
from project.Errors import Missing_Input_Columns, No_Such_Column, Non_Unique_Key, Unmatched_Rows_In_Lookup
## PRIVATE
replace : Table -> (Table | Map) -> (Text | Integer | By_Type | Vector (Text | Integer | By_Type)) -> (Text | Integer | Nothing) -> (Text | Integer | Nothing) -> Boolean -> Problem_Behavior -> Table ! Missing_Input_Columns | Non_Unique_Key | Unmatched_Rows_In_Lookup
replace : Table -> (Table | Dictionary) -> (Text | Integer | By_Type | Vector (Text | Integer | By_Type)) -> (Text | Integer | Nothing) -> (Text | Integer | Nothing) -> Boolean -> Problem_Behavior -> Table ! Missing_Input_Columns | Non_Unique_Key | Unmatched_Rows_In_Lookup
replace base_table lookup_table columns:(Text | Integer | By_Type | Vector (Text | Integer | By_Type)) from_column:(Text | Integer | Nothing)=Nothing to_column:(Text | Integer | Nothing)=Nothing allow_unmatched_rows:Boolean=True on_problems:Problem_Behavior=..Report_Warning =
case columns of
_ : Vector ->
@ -20,8 +20,8 @@ replace base_table lookup_table columns:(Text | Integer | By_Type | Vector (Text
_ ->
column = columns
case lookup_table of
_ : Map ->
if from_column.is_nothing.not || to_column.is_nothing.not then Error.throw (Illegal_Argument.Error "If a Map is provided as the lookup_table, then from_column and to_column should not also be specified.") else
_ : Dictionary ->
if from_column.is_nothing.not || to_column.is_nothing.not then Error.throw (Illegal_Argument.Error "If a Dictionary is provided as the lookup_table, then from_column and to_column should not also be specified.") else
handle_empty_lookup_table ~action =
if lookup_table.is_empty.not then action else
## If the lookup table is empty but the base table is
@ -33,7 +33,7 @@ replace base_table lookup_table columns:(Text | Integer | By_Type | Vector (Text
a_key_value = base_table.at column . at 0
Error.throw (Unmatched_Rows_In_Lookup.Error [a_key_value])
handle_empty_lookup_table <|
base_table.replace (base_table.make_table_from_map lookup_table 'from' 'to') column 'from' 'to' allow_unmatched_rows=allow_unmatched_rows on_problems=on_problems
base_table.replace (base_table.make_table_from_dictionary lookup_table 'from' 'to') column 'from' 'to' allow_unmatched_rows=allow_unmatched_rows on_problems=on_problems
_ ->
from_column_resolved = from_column.if_nothing 0
to_column_resolved = to_column.if_nothing 1
@ -52,7 +52,7 @@ replace base_table lookup_table columns:(Text | Integer | By_Type | Vector (Text
## Create a lookup table with just `to_column` and `from_column`,
renamed to match the base table's `column` and its duplicate,
respectively.
lookup_table_renamed = selected_lookup_columns . rename_columns (Map.from_vector [[from_column_resolved, duplicate_key_column_name], [to_column_resolved, column]])
lookup_table_renamed = selected_lookup_columns . rename_columns (Dictionary.from_vector [[from_column_resolved, duplicate_key_column_name], [to_column_resolved, column]])
warn_if_empty result_table = if lookup_table_renamed.row_count != 0 then result_table else Warning.attach (Empty_Error.Error "lookup_table") result_table

View File

@ -91,7 +91,7 @@ regex_to_column_names pattern original_column_name =
group_nums_to_names = pattern.group_nums_to_names
unnamed_group_numbers = 1.up_to pattern.group_count . filter i-> group_nums_to_names.contains_key i . not
group_number_to_column_name_suffix = Map.from_vector <| unnamed_group_numbers.zip (0.up_to unnamed_group_numbers.length)
group_number_to_column_name_suffix = Dictionary.from_vector <| unnamed_group_numbers.zip (0.up_to unnamed_group_numbers.length)
Vector.new (pattern.group_count-1) i->
# explicit groups start at 1

View File

@ -112,7 +112,7 @@ type Table_Column_Helper
remove_columns self (selectors:(Text | Integer | Regex | Vector)) (case_sensitivity:Case_Sensitivity) (error_on_missing_columns:Boolean) (on_problems:Problem_Behavior) =
problem_builder = Problem_Builder.new error_on_missing_columns=error_on_missing_columns
selection = self.select_columns_helper selectors case_sensitivity False problem_builder
selected_names = Map.from_vector (selection.map column-> [column.name, True])
selected_names = Dictionary.from_vector (selection.map column-> [column.name, True])
result = self.columns.filter column->
should_be_removed = selected_names.get column.name False
should_be_removed.not
@ -160,7 +160,7 @@ type Table_Column_Helper
problem_builder = Problem_Builder.new error_on_missing_columns=error_on_missing_columns
selection = self.select_columns_helper selectors case_sensitivity True problem_builder
problem_builder.attach_problems_before on_problems <|
selected_names = Map.from_vector (selection.map column-> [column.name, True])
selected_names = Dictionary.from_vector (selection.map column-> [column.name, True])
other_columns = self.columns.filter column->
is_selected = selected_names.get column.name False
is_selected.not
@ -203,8 +203,8 @@ type Table_Column_Helper
_ -> [selectors]
selected_columns = vector.map resolve_selector . flatten
if reorder then selected_columns.distinct on=_.name else
map = Map.from_vector (selected_columns.map column-> [column.name, True]) error_on_duplicates=False
self.columns.filter column-> map.contains_key column.name
dict = Dictionary.from_vector (selected_columns.map column-> [column.name, True]) error_on_duplicates=False
self.columns.filter column-> dict.contains_key column.name
## PRIVATE
A helper function which selects a single column from the table.
@ -289,14 +289,14 @@ type Table_Column_Helper
A helper function which takes a Table like object and a Table for a name
mapping and returns a new Table with the columns renamed according to the
mapping.
read_name_map_from_table : Table -> Vector | Map ! Illegal_Argument
read_name_map_from_table column_map:Table = case column_map.column_count of
read_name_mapping_from_table : Table -> Vector | Dictionary ! Illegal_Argument
read_name_mapping_from_table column_map:Table = case column_map.column_count of
1 ->
col = column_map.first_column
if col.value_type.is_text then col.to_vector else
Error.throw (Illegal_Argument.Error "Expected a table with one or two columns of text values.")
2 ->
if column_map.first_column.value_type.is_text && (column_map.at 1).value_type.is_text then Map.from_vector column_map.rows else
if column_map.first_column.value_type.is_text && (column_map.at 1).value_type.is_text then Dictionary.from_vector column_map.rows else
Error.throw (Illegal_Argument.Error "Expected a table with one or two columns of text values.")
_ -> Error.throw (Illegal_Argument.Error "Expected a table with one or two columns of text values.")
@ -322,9 +322,9 @@ read_name_map_from_table column_map:Table = case column_map.column_count of
operation. By default, a warning is issued, but the operation proceeds.
If set to `Report_Error`, the operation fails with a dataflow error.
If set to `Ignore`, the operation proceeds without errors or warnings.
rename_columns : Column_Naming_Helper -> Vector -> Map (Text | Integer | Regex) Text | Vector Text -> Case_Sensitivity -> Boolean -> Problem_Behavior -> Map Text Text
rename_columns (naming_helper : Column_Naming_Helper) (internal_columns:Vector) (mapping:(Map | Vector)) (case_sensitivity:Case_Sensitivity) (error_on_missing_columns:Boolean) (on_problems:Problem_Behavior) =
## Convert Vector of Pairs to Map
rename_columns : Column_Naming_Helper -> Vector -> Dictionary (Text | Integer | Regex) Text | Vector Text -> Case_Sensitivity -> Boolean -> Problem_Behavior -> Dictionary Text Text
rename_columns (naming_helper : Column_Naming_Helper) (internal_columns:Vector) (mapping:(Dictionary | Vector)) (case_sensitivity:Case_Sensitivity) (error_on_missing_columns:Boolean) (on_problems:Problem_Behavior) =
## Convert Vector of Pairs to Dictionary
is_vec_pairs = mapping.is_a Vector && mapping.length > 0 && (mapping.first.is_a Text . not)
case is_vec_pairs of
True ->
@ -333,8 +333,8 @@ rename_columns (naming_helper : Column_Naming_Helper) (internal_columns:Vector)
is_valid_key k = k.is_a Integer || k.is_a Text || k.is_a Regex
all_pairs = mapping.all p-> (is_valid_row p) && p.length == 2 && (is_valid_key p.first) && p.second.is_a Text
if all_pairs.not then Error.throw (Illegal_Argument.Error "mapping is not a Vector of old name to new name.") else
## Attempt to treat as Map
map = Map.from_vector mapping error_on_duplicates=False
## Attempt to treat as Dictionary
map = Dictionary.from_vector mapping error_on_duplicates=False
if map.length == mapping.length then rename_columns naming_helper internal_columns map case_sensitivity error_on_missing_columns on_problems else
duplicates = mapping.duplicates on=_.first . map p->p.first.to_text . distinct
duplicate_text = if duplicates.length < 5 then duplicates.to_vector . join ", " else
@ -356,7 +356,7 @@ rename_columns (naming_helper : Column_Naming_Helper) (internal_columns:Vector)
problem_builder.report_other_warning (Too_Many_Column_Names_Provided.Error (vec.drop (..First col_count)))
vec.take (..First col_count)
internal_columns.take good_names.length . zip good_names
_ : Map ->
_ : Dictionary ->
resolve_rename selector replacement = case selector of
ix : Integer -> if is_index_valid internal_columns.length ix then [Pair.new (internal_columns.at ix) replacement] else
problem_builder.report_oob_indices [ix]
@ -378,7 +378,7 @@ rename_columns (naming_helper : Column_Naming_Helper) (internal_columns:Vector)
naming_helper.validate_many_column_names all_new_names <|
## Resolves actual new names for renamed columns.
If a column received ambiguous new names, an error is raised.
name_map = columns_with_new_names.fold Map.empty current-> pair->
name_dict = columns_with_new_names.fold Dictionary.empty current-> pair->
old_name = pair.first.name
case current.contains_key old_name of
False -> current.insert old_name pair.second
@ -387,12 +387,12 @@ rename_columns (naming_helper : Column_Naming_Helper) (internal_columns:Vector)
Error.throw (Ambiguous_Column_Rename.Error old_name new_names)
## Renamed names take precedence, so we first deduplicate those.
resolved_name_map = name_map.map unique.make_unique
resolved_name_dict = name_dict.map unique.make_unique
## Then we ensure that the names of not-renamed columns are also unique and
return the effective column names for each column.
not_renamed = internal_columns.filter c-> resolved_name_map.contains_key c.name . not
new_column_names = not_renamed.fold resolved_name_map current-> column->
not_renamed = internal_columns.filter c-> resolved_name_dict.contains_key c.name . not
new_column_names = not_renamed.fold resolved_name_dict current-> column->
new_name = unique.make_unique column.name
current.insert column.name new_name
@ -443,7 +443,7 @@ select_indices_reordering vector indices =
The elements are returned in the same order as they appeared in the original
vector.
select_indices_preserving_order vector indices =
indices_to_keep = Map.from_vector (indices.map i-> [i, True])
indices_to_keep = Dictionary.from_vector (indices.map i-> [i, True])
vector.filter_with_index ix-> _->
indices_to_keep.get ix False

View File

@ -98,10 +98,10 @@ type Unique_Name_Strategy
## PRIVATE
A mapping of original names to their truncated counterpart.
truncated_names : Map Text Text
truncated_names : Dictionary Text Text
truncated_names self =
entries = Vector.from_polyglot_array self.deduplicator.getTruncatedNames
Map.from_vector <| entries.map e-> [e.getLeft, e.getRight]
Dictionary.from_vector <| entries.map e-> [e.getLeft, e.getRight]
## PRIVATE
ADVANCED

View File

@ -142,7 +142,7 @@ type Column_Set
## PRIVATE
Returns a map indicating in how many tables did a column with a given name appear.
find_column_counts tables =
tables.fold Map.empty current->table->
tables.fold Dictionary.empty current->table->
table.columns.fold current counts-> column->
name=column.name
new_count = counts.get name 0 + 1
@ -153,7 +153,7 @@ find_column_counts tables =
starting from the first table.
distinct_columns_in_appearance_order tables =
Vector.build names_builder->
tables.fold Map.empty current-> table->
tables.fold Dictionary.empty current-> table->
table.columns.fold current seen_names-> column->
name = column.name
if seen_names.contains_key name then seen_names else

View File

@ -53,11 +53,11 @@ type Row
## GROUP Standard.Base.Conversions
ICON convert
Gets the row as a Map.
to_map : Map
to_map self =
Gets the row as a Dictionary.
to_dictionary : Dictionary
to_dictionary self =
pairs = self.table.column_names.map n-> [n, self.get n]
Map.from_vector pairs
Dictionary.from_vector pairs
## PRIVATE
Converts this row into a JS_Object.

View File

@ -680,17 +680,17 @@ type Table
> Example
Rename the "Alpha" column to "Beta"
table.rename_columns (Map.from_vector [["Alpha", "Beta"]])
table.rename_columns (Dictionary.from_vector [["Alpha", "Beta"]])
> Example
Rename the last column to "LastColumn"
table.rename_columns (Map.from_vector [[-1, "LastColumn"]])
table.rename_columns (Dictionary.from_vector [[-1, "LastColumn"]])
> Example
Rename the "Alpha" column to "Beta" and last column to "LastColumn"
table.rename_columns (Map.from_vector [["Alpha", "Beta"], [-1, "LastColumn"]])
table.rename_columns (Dictionary.from_vector [["Alpha", "Beta"], [-1, "LastColumn"]])
> Example
Rename the first column to "FirstColumn"
@ -705,12 +705,12 @@ type Table
> Example
For all columns starting with the prefix `name=`, replace it with `key:`.
table.rename_columns (Map.from_vector [["name=(.*)".to_regex, "key:$1"]])
table.rename_columns (Dictionary.from_vector [["name=(.*)".to_regex, "key:$1"]])
@column_map Widget_Helpers.make_rename_name_vector_selector
rename_columns : Map (Text | Integer | Regex) Text | Vector Text | Vector Vector -> Case_Sensitivity -> Boolean -> Problem_Behavior -> Table ! Missing_Input_Columns | Ambiguous_Column_Rename | Too_Many_Column_Names_Provided | Invalid_Column_Names | Duplicate_Output_Column_Names
rename_columns self (column_map:(Table | Map | Vector)=["Column"]) (case_sensitivity:Case_Sensitivity=..Default) (error_on_missing_columns:Boolean=True) (on_problems:Problem_Behavior=..Report_Warning) = case column_map of
rename_columns : Table | Dictionary (Text | Integer | Regex) Text | Vector Text | Vector Vector -> Case_Sensitivity -> Boolean -> Problem_Behavior -> Table ! Missing_Input_Columns | Ambiguous_Column_Rename | Too_Many_Column_Names_Provided | Invalid_Column_Names | Duplicate_Output_Column_Names
rename_columns self (column_map:(Table | Dictionary | Vector)=["Column"]) (case_sensitivity:Case_Sensitivity=..Default) (error_on_missing_columns:Boolean=True) (on_problems:Problem_Behavior=..Report_Warning) = case column_map of
_ : Table ->
resolved = Table_Helpers.read_name_map_from_table column_map
resolved = Table_Helpers.read_name_mapping_from_table column_map
self.rename_columns resolved case_sensitivity error_on_missing_columns on_problems
_ ->
new_names = Table_Helpers.rename_columns self.column_naming_helper self.columns column_map case_sensitivity error_on_missing_columns on_problems
@ -1123,9 +1123,9 @@ type Table
no_columns_problem_behavior = case error_on_missing_columns of
True -> Problem_Behavior.Report_Error
False -> on_problems
no_columns_problem_behavior.attach_problem_before No_Input_Columns_Selected Map.empty
no_columns_problem_behavior.attach_problem_before No_Input_Columns_Selected Dictionary.empty
False ->
Map.from_vector <| selected_columns.map c-> [c.name, True]
Dictionary.from_vector <| selected_columns.map c-> [c.name, True]
new_columns = self.columns.map on_problems=No_Wrap column-> if selected_column_names.contains_key column.name . not then column else
Value_Type.expect_text column <|
@ -1222,9 +1222,9 @@ type Table
no_columns_problem_behavior = case error_on_missing_columns of
True -> Problem_Behavior.Report_Error
False -> on_problems
no_columns_problem_behavior.attach_problem_before No_Input_Columns_Selected Map.empty
no_columns_problem_behavior.attach_problem_before No_Input_Columns_Selected Dictionary.empty
False ->
Map.from_vector <| selected_columns.map c-> [c.name, True]
Dictionary.from_vector <| selected_columns.map c-> [c.name, True]
new_columns = self.columns.map column-> if selected_column_names.contains_key column.name . not then column else
column.format format locale
@ -1887,22 +1887,22 @@ type Table
Warning.set result []
## PRIVATE
A helper that creates a two-column table from a Map.
A helper that creates a two-column table from a Dictionary.
The keys of the `Map` become the first column, with name
`key_column_name`, and the values of the `Map` become the second column,
with name `value_column_name`.
The keys of the `Dictionary` become the first column, with name
`key_column_name`, and the values become the second column, with name
`value_column_name`.
For the in-memory database, the `Map` can be empty. For the database
backends, it must not be empty.
For the in-memory database, the `Dictionary` can be empty. For the
database backends, it must not be empty.
Arguments:
- map: The `Map` to create the table from.
- dict: The `Dictionary` to create the table from.
- key_column_name: The name to use for the first column.
- value_column_name: The name to use for the second column.
make_table_from_map : Map Any Any -> Text -> Text -> Table
make_table_from_map self map key_column_name value_column_name =
keys_and_values = map.to_vector
make_table_from_dictionary : Dictionary Any Any -> Text -> Text -> Table
make_table_from_dictionary self dict key_column_name value_column_name =
keys_and_values = dict.to_vector
self.make_table_from_vectors [keys_and_values.map .first, keys_and_values.map .second] [key_column_name, value_column_name]
## PRIVATE
@ -2283,8 +2283,8 @@ type Table
@columns (Widget_Helpers.make_column_name_multi_selector add_regex=True add_by_type=True)
@from_column Widget.Text_Input
@to_column Widget.Text_Input
replace : (Table | Map) -> (Text | Integer | Regex | By_Type | Vector (Text | Integer | Regex | By_Type)) -> (Text | Integer | Nothing) -> (Text | Integer | Nothing) -> Boolean -> Problem_Behavior -> Table ! Missing_Input_Columns | Non_Unique_Key | Unmatched_Rows_In_Lookup
replace self lookup_table:(Table | Map) columns:(Text | Integer | Regex | By_Type | Vector (Text | Integer | Regex | By_Type)) from_column:(Text | Integer | Nothing)=Nothing to_column:(Text | Integer | Nothing)=Nothing allow_unmatched_rows:Boolean=True on_problems:Problem_Behavior=..Report_Warning =
replace : (Table | Dictionary) -> (Text | Integer | Regex | By_Type | Vector (Text | Integer | Regex | By_Type)) -> (Text | Integer | Nothing) -> (Text | Integer | Nothing) -> Boolean -> Problem_Behavior -> Table ! Missing_Input_Columns | Non_Unique_Key | Unmatched_Rows_In_Lookup
replace self lookup_table:(Table | Dictionary) columns:(Text | Integer | Regex | By_Type | Vector (Text | Integer | Regex | By_Type)) from_column:(Text | Integer | Nothing)=Nothing to_column:(Text | Integer | Nothing)=Nothing allow_unmatched_rows:Boolean=True on_problems:Problem_Behavior=..Report_Warning =
Replace_Helpers.replace self lookup_table columns from_column to_column allow_unmatched_rows on_problems
## ALIAS join by row position
@ -2566,7 +2566,7 @@ type Table
id_columns = columns_helper.select_columns_helper key_columns Case_Sensitivity.Default False problem_builder
selected_names = Map.from_vector (id_columns.map column-> [column.name, True])
selected_names = Dictionary.from_vector (id_columns.map column-> [column.name, True])
data = columns_helper.internal_columns.filter column->(selected_names.get column.name False . not)
java_data = data.map c->c.java_column
@ -3183,13 +3183,6 @@ concat_columns column_set all_tables result_type result_row_count needs_cast on_
sealed_storage = storage_builder.seal
Column.from_storage column_set.name sealed_storage
## PRIVATE
A helper that creates a two-column table from a map.
map_to_lookup_table : Map Any Any -> Text -> Text -> Table
map_to_lookup_table map key_column value_column =
keys_and_values = map.to_vector
Table.new [[key_column, keys_and_values.map .first], [value_column, keys_and_values.map .second]]
## PRIVATE
Conversion method to a Table from a Column.
Table.from (that:Column) = that.to_table

View File

@ -98,7 +98,7 @@ print_single_result (test_result : Test_Result) (config : Suite_Config) =
print_report : Vector Test_Result -> Suite_Config -> (StringBuilder | Nothing) -> Nothing
print_report (test_results : Vector Test_Result) (config : Suite_Config) (builder : (StringBuilder | Nothing)) =
distinct_group_names = test_results.map (_.group_name) . distinct
results_per_group = distinct_group_names.fold Map.empty acc-> group_name->
results_per_group = distinct_group_names.fold Dictionary.empty acc-> group_name->
group_results = test_results.filter res->
res.group_name == group_name
assert (group_results.length > 0)

View File

@ -173,7 +173,7 @@ limit_data limit data = case limit of
bounds = case data.fold_with_index (Extreme.Value first first first first) update_extreme of
Extreme.Value min_x max_x min_y max_y -> [min_x, max_x, min_y, max_y]
_ -> []
extreme = Map.from_vector bounds error_on_duplicates=False . values
extreme = Dictionary.from_vector bounds error_on_duplicates=False . values
if limit <= extreme.length then extreme.take (..First limit) else
extreme + data.take (..Sample (limit - extreme.length))

View File

@ -27,9 +27,9 @@ prepare_visualization y max_rows=1000 =
result = case x of
_ : Vector -> make_json_for_vector x max_rows
_ : Array -> prepare_visualization x.to_vector max_rows
_ : Map -> make_json_for_map x max_rows
_ : Dictionary -> make_json_for_dictionary x max_rows
_ : JS_Object -> make_json_for_js_object x max_rows
_ : Row -> make_json_for_map x.to_map max_rows "column"
_ : Row -> make_json_for_dictionary x.to_dictionary max_rows "column"
_ : Column -> prepare_visualization x.to_table max_rows
_ : Table ->
dataframe = x.take max_rows
@ -98,7 +98,7 @@ make_json_for_object_matrix current vector idx=0 = if idx == vector.length then
_ : Date_Time -> False
_ : Duration -> False
_ : Period -> False
_ : Map ->
_ : Dictionary ->
pairs = row.keys.map k-> [k.to_text, make_json_for_value (row.get k)]
JS_Object.from_pairs pairs
_ : Row ->
@ -115,13 +115,13 @@ make_json_for_object_matrix current vector idx=0 = if idx == vector.length then
@Tail_Call make_json_for_object_matrix current vector idx+1
## PRIVATE
Render Map to JSON
make_json_for_map : Map -> Integer -> Text -> JS_Object
make_json_for_map map max_items key_name="key" =
Render Dictionary to JSON
make_json_for_dictionary : Dictionary -> Integer -> Text -> JS_Object
make_json_for_dictionary dict max_items key_name="key" =
header = ["header", [key_name, "value"]]
all_rows = ["all_rows_count", map.size]
map_vector = Warning.clear (map.to_vector.take max_items)
mapped = map_vector . map p-> [p.first.to_text, make_json_for_value p.second]
all_rows = ["all_rows_count", dict.size]
as_vector = Warning.clear (dict.to_vector.take max_items)
mapped = as_vector . map p-> [p.first.to_text, make_json_for_value p.second]
data = ["data", [mapped.map .first, mapped.map .second]]
JS_Object.from_pairs [header, data, all_rows, ["type", "Map"]]
@ -187,7 +187,7 @@ make_json_for_value val level=0 = case val of
prepared = if val.length > 5 then truncated + ["… " + (val.length - 5).to_text+ " items"] else truncated
"[" + (prepared.join ", ") + "]"
_ : Array -> make_json_for_value val.to_vector level
_ : Map ->
_ : Dictionary ->
if level != 0 then "{… "+val.size.to_text+" items}" else
truncated = val.keys.take 5 . map k-> k.to_text + ": " + (make_json_for_value (val.get k) level+1).to_text
prepared = if val.length > 5 then truncated + ["… " + (val.length - 5).to_text+ " items"] else truncated

View File

@ -64,12 +64,12 @@ public class ConversionMethodTests {
String src =
"""
polyglot java import java.util.Map as Java_Map
import Standard.Base.Data.Map.Map
import Standard.Base.Data.Dictionary.Dictionary
type Foo
Mk_Foo data
Foo.from (that:Map) = Foo.Mk_Foo that
Foo.from (that:Dictionary) = Foo.Mk_Foo that
main =
jmap = Java_Map.of "A" 1 "B" 2 "C" 3
@ -83,7 +83,7 @@ public class ConversionMethodTests {
public void testDispatchOnJSMap() {
String src =
"""
import Standard.Base.Data.Map.Map
import Standard.Base.Data.Dictionary.Dictionary
foreign js js_map = '''
let m = new Map()
@ -94,7 +94,7 @@ public class ConversionMethodTests {
type Foo
Mk_Foo data
Foo.from (that:Map) = Foo.Mk_Foo that
Foo.from (that:Dictionary) = Foo.Mk_Foo that
main =
Foo.from js_map . data . size

View File

@ -285,66 +285,7 @@ main = Nothing
.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "sn");
var sb = new StringBuilder();
for (var v : g.allValues()) {
var simpleName = sn.execute(v).asString();
if (v.isNumber()) {
var ok =
switch (simpleName) {
case "Integer", "Float" -> true;
default -> false;
};
assertTrue("Unexpected simple name for number: " + simpleName, ok);
continue;
}
var meta = v.getMetaObject();
var metaName = meta != null ? meta.getMetaSimpleName() : "null";
if (!simpleName.equals(metaName)) {
if (v.isHostObject()) {
if (v.hasArrayElements()) {
assertEquals("Array", simpleName);
continue;
}
if (v.hasHashEntries()) {
assertEquals("Map", simpleName);
continue;
}
}
if (v.isString()) {
assertEquals("Text", simpleName);
continue;
}
if (v.isDuration()) {
assertEquals("Duration", simpleName);
continue;
}
if (v.isDate() && v.isTime()) {
assertEquals("Date_Time", simpleName);
continue;
}
if (v.isTimeZone()) {
assertEquals("Time_Zone", simpleName);
continue;
}
if (v.isDate()) {
assertEquals("Date", simpleName);
continue;
}
if (v.isTime()) {
assertEquals("Time_Of_Day", simpleName);
continue;
}
if (v.isNull()) {
assertEquals("Nothing", simpleName);
continue;
}
sb.append("\n")
.append("Simple names shall be the same for ")
.append(v)
.append(" get_simple_type_name: ")
.append(simpleName)
.append(" getMetaSimpleName: ")
.append(metaName);
}
compareQualifiedNameOfValue(sn, v, sb);
}
if (!sb.isEmpty()) {
var lines = sb.toString().lines().count() - 1;
@ -353,6 +294,77 @@ main = Nothing
}
}
private boolean compareQualifiedNameOfValue(Value sn, Value v, StringBuilder sb) {
var simpleName = sn.execute(v).asString();
if (v.isNumber()) {
var ok =
switch (simpleName) {
case "Integer", "Float" -> true;
default -> false;
};
assertTrue("Unexpected simple name for number: " + simpleName, ok);
return true;
}
var meta = v.getMetaObject();
var metaName = meta != null ? meta.getMetaSimpleName() : "null";
if (!simpleName.equals(metaName)) {
if (v.isHostObject()) {
if (v.hasArrayElements()) {
assertEquals("Array", simpleName);
return true;
}
if (v.hasHashEntries()) {
assertEquals("Dictionary", simpleName);
return true;
}
}
if (v.hasMembers() && v.getMember("__proto__") != null) {
if (v.hasHashEntries()) {
assertEquals("JavaScript hash map is called Map", "Map", metaName);
assertEquals(
"JavaScript hash map is seen as Dictionary by Enso", "Dictionary", simpleName);
return true;
}
}
if (v.isString()) {
assertEquals("Text", simpleName);
return true;
}
if (v.isDuration()) {
assertEquals("Duration", simpleName);
return true;
}
if (v.isDate() && v.isTime()) {
assertEquals("Date_Time", simpleName);
return true;
}
if (v.isTimeZone()) {
assertEquals("Time_Zone", simpleName);
return true;
}
if (v.isDate()) {
assertEquals("Date", simpleName);
return true;
}
if (v.isTime()) {
assertEquals("Time_Of_Day", simpleName);
return true;
}
if (v.isNull()) {
assertEquals("Nothing", simpleName);
return true;
}
sb.append("\n")
.append("Simple names shall be the same for ")
.append(v)
.append(" get_simple_type_name: ")
.append(simpleName)
.append(" getMetaSimpleName: ")
.append(metaName);
}
return false;
}
@Test
public void compareQualifiedAndSimpleTypeNameForTypes() throws Exception {
var g = generator();

View File

@ -303,9 +303,13 @@ public final class ValuesGenerator {
}
public Value typeMap() {
return v("typeMap", """
import Standard.Base.Data.Map.Map
""", "Map").type();
return v(
"typeMap",
"""
import Standard.Base.Data.Dictionary.Dictionary
""",
"Dictionary")
.type();
}
public Value typeWarning() {
@ -679,28 +683,28 @@ public final class ValuesGenerator {
if (languages.contains(Language.ENSO)) {
var imports =
"""
import Standard.Base.Data.Map.Map
import Standard.Base.Data.Dictionary.Dictionary
import Standard.Base.Nothing.Nothing
""";
for (var expr :
List.of(
"Map.empty",
"Map.singleton Nothing Nothing",
"Map.singleton Nothing 'my_value'",
"Map.singleton 'my_value' Nothing",
"Map.singleton 1 1",
"Map.singleton 'C' 3",
"Map.singleton 'C' 43",
"Map.empty.insert 'A' 10 . insert 'B' 20",
"Dictionary.empty",
"Dictionary.singleton Nothing Nothing",
"Dictionary.singleton Nothing 'my_value'",
"Dictionary.singleton 'my_value' Nothing",
"Dictionary.singleton 1 1",
"Dictionary.singleton 'C' 3",
"Dictionary.singleton 'C' 43",
"Dictionary.empty.insert 'A' 10 . insert 'B' 20",
// ((int) 'A') + ((int) 'B') = 131 ; codePoint(131) = \203
"Map.singleton '\203' 30",
"Map.singleton Map.empty 1",
"Map.singleton Map.empty Map.empty",
"Map.empty.insert 1 1 . insert 2 2",
"Map.empty.insert Nothing 'val' . insert 'key' 42",
"Map.empty.insert 'A' 1 . insert 'B' 2 . insert 'C' 3",
"Map.empty.insert 'C' 3 . insert 'B' 2 . insert 'A' 1")) {
collect.add(v("maps-" + expr, imports, expr, "Map").type());
"Dictionary.singleton '\203' 30",
"Dictionary.singleton Dictionary.empty 1",
"Dictionary.singleton Dictionary.empty Dictionary.empty",
"Dictionary.empty.insert 1 1 . insert 2 2",
"Dictionary.empty.insert Nothing 'val' . insert 'key' 42",
"Dictionary.empty.insert 'A' 1 . insert 'B' 2 . insert 'C' 3",
"Dictionary.empty.insert 'C' 3 . insert 'B' 2 . insert 'A' 1")) {
collect.add(v("maps-" + expr, imports, expr, "Dictionary").type());
}
}
if (languages.contains(Language.JAVA)) {

View File

@ -358,7 +358,10 @@ public abstract class InvokeConversionNode extends BaseNode {
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
Function function =
conversionResolverNode.expectNonNull(
thatMap, extractType(self), EnsoContext.get(this).getBuiltins().map(), conversion);
thatMap,
extractType(self),
EnsoContext.get(this).getBuiltins().dictionary(),
conversion);
return invokeFunctionNode.execute(function, frame, state, arguments);
}

View File

@ -637,7 +637,7 @@ public abstract class InvokeMethodNode extends BaseNode {
@Shared("warnings") @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Shared("methodResolverNode") @Cached MethodResolverNode methodResolverNode) {
var ctx = EnsoContext.get(this);
var hashMapType = ctx.getBuiltins().map();
var hashMapType = ctx.getBuiltins().dictionary();
var function = methodResolverNode.expectNonNull(self, hashMapType, symbol);
arguments[0] = self;
return invokeFunctionNode.execute(function, frame, state, arguments);

View File

@ -104,7 +104,7 @@ public final class Builtins {
private final Builtin text;
private final Builtin array;
private final Builtin vector;
private final Builtin map;
private final Builtin dictionary;
private final Builtin dataflowError;
private final Builtin ref;
private final Builtin managedResource;
@ -155,7 +155,7 @@ public final class Builtins {
text = builtins.get(Text.class);
array = builtins.get(Array.class);
vector = builtins.get(Vector.class);
map = builtins.get(org.enso.interpreter.node.expression.builtin.Map.class);
dictionary = builtins.get(org.enso.interpreter.node.expression.builtin.Dictionary.class);
dataflowError = builtins.get(org.enso.interpreter.node.expression.builtin.Error.class);
ref = builtins.get(Ref.class);
managedResource = builtins.get(ManagedResource.class);
@ -691,8 +691,8 @@ public final class Builtins {
return vector.getType();
}
public Type map() {
return map.getType();
public Type dictionary() {
return dictionary.getType();
}
/**

View File

@ -35,7 +35,7 @@ import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
*/
@ExportLibrary(TypesLibrary.class)
@ExportLibrary(InteropLibrary.class)
@Builtin(stdlibName = "Standard.Base.Data.Map.Map", name = "Map")
@Builtin(stdlibName = "Standard.Base.Data.Dictionary.Dictionary", name = "Dictionary")
public final class EnsoHashMap implements EnsoObject {
private final EnsoHashMapBuilder mapBuilder;
private final int generation;
@ -150,7 +150,7 @@ public final class EnsoHashMap implements EnsoObject {
@ExportMessage(library = TypesLibrary.class)
Type getType(@Bind("$node") Node node) {
return EnsoContext.get(node).getBuiltins().map();
return EnsoContext.get(node).getBuiltins().dictionary();
}
@ExportMessage
@ -160,7 +160,7 @@ public final class EnsoHashMap implements EnsoObject {
@ExportMessage
Type getMetaObject(@Bind("$node") Node node) {
return EnsoContext.get(node).getBuiltins().map();
return EnsoContext.get(node).getBuiltins().dictionary();
}
@ExportMessage

View File

@ -9,7 +9,7 @@ import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(
type = "Map",
type = "Dictionary",
name = "contains_key",
description =
"""

View File

@ -19,11 +19,11 @@ import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.state.State;
@BuiltinMethod(
type = "Map",
type = "Dictionary",
name = "get_builtin",
description =
"""
Gets a value from the map on the specified key, or the given default.
Gets a value from the dictionary on the specified key, or the given default.
""",
autoRegister = false,
inlineable = true)

View File

@ -18,7 +18,7 @@ import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(
type = "Map",
type = "Dictionary",
name = "insert",
description =
"""

View File

@ -20,7 +20,7 @@ import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.error.DataflowError;
@BuiltinMethod(
type = "Map",
type = "Dictionary",
name = "remove_builtin",
description = """
Removes an entry from this map specified with the key.

View File

@ -12,7 +12,7 @@ import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(
type = "Map",
type = "Dictionary",
name = "size",
description = "Returns the number of entries in this hash map",
autoRegister = false)

View File

@ -13,7 +13,7 @@ import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.EnsoContext;
@BuiltinMethod(
type = "Map",
type = "Dictionary",
name = "to_text",
description = """
Returns text representation of this hash map

View File

@ -18,7 +18,7 @@ import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(
type = "Map",
type = "Dictionary",
name = "to_vector",
description =
"""

View File

@ -114,7 +114,7 @@ public abstract class TypeOfNode extends Node {
@Specialization(guards = {"type.isMap()"})
Type doPolygotMap(Interop type, Object value) {
return EnsoContext.get(this).getBuiltins().map();
return EnsoContext.get(this).getBuiltins().dictionary();
}
@Specialization(guards = {"type.isString()"})

View File

@ -169,7 +169,7 @@ add_specs suite_builder =
suite_builder.group "S3.head (bucket)" pending=api_pending group_builder->
group_builder.specify "should be able to head a bucket" <|
S3.head bucket_name credentials=test_credentials . should_be_a Map
S3.head bucket_name credentials=test_credentials . should_be_a Dictionary
S3.head not_a_bucket_name credentials=test_credentials . should_fail_with S3_Bucket_Not_Found
suite_builder.group "S3.read_bucket" pending=api_pending group_builder->

View File

@ -0,0 +1,633 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Errors.No_Such_Key.No_Such_Key
from Standard.Test import all
polyglot java import java.util.Map as JavaMap
polyglot java import org.enso.base.file_system.File_Utils
## Type that violates reflexivity
type My_Nan
Value comment:Text
type My_Nan_Comparator
compare _ _ = Nothing
hash _ = 0
Comparable.from (_:My_Nan) = My_Nan_Comparator
type My_Key
Value hash_code:Integer value:Text idx:Integer
type My_Key_Comparator
# Comparison ignores idx field
compare x y =
if x.hash_code != y.hash_code then Nothing else
if x.value == y.value then Ordering.Equal else Nothing
hash x = x.hash_code
Comparable.from (_:My_Key) = My_Key_Comparator
foreign js js_str str = """
return new String(str)
foreign js js_null = """
return null
foreign js js_empty_dict = """
return new Map()
foreign python py_empty_dict = """
return {}
foreign js js_dict_from_vec vec = """
dict = new Map()
for (let i = 0; i < vec.length; i += 2) {
dict.set(vec[i], vec[i+1])
}
return dict
foreign python py_none = """
return None
foreign python py_dict_from_vec vec = """
d = {}
for i in range(0, len(vec), 2):
d[vec[i]] = vec[i + 1]
return d
foreign python py_dict_from_map map = """
d = dict()
for key in map.__iter__():
d[key] = map[key]
return d
foreign python py_vec_from_map map = """
vec = []
for key in map.__iter__():
value = map[key]
vec.append([key, value])
return vec
# Should throw error - updating immutable map from Enso
foreign python py_update_dict map key val = """
map[key] = val
foreign python py_wrapper obj = """
class MyClass:
def __init__(self, obj):
self.data = obj
return MyClass(obj)
pending_python_missing = if Polyglot.is_language_installed "python" then Nothing else "Can't run Python tests, Python is not installed."
type Child
Value data
type Parent
Value child
type GrandParent
Value parent
add_specs suite_builder =
languages = Vector.build builder->
builder . append ["Enso", _-> Dictionary.empty, Nothing]
builder . append ["Java", _-> JavaMap.of, Nothing]
builder . append ["JavaScript", _-> js_empty_dict, Nothing]
builder . append ["Python", _-> py_empty_dict, pending_python_missing]
languages.each entry->
lang = entry.get 0
empty_dict_fn = entry.get 1
pending = entry.get 2
add_common_specs suite_builder lang pending empty_dict_fn
suite_builder.group "Enso Dictionaries" group_builder->
group_builder.specify "should use proper hash code for keys" <|
single_key_dict key = Dictionary.singleton key 42
grand_parent_1 = GrandParent.Value (Parent.Value (Child.Value 2))
grand_parent_2 = GrandParent.Value (Parent.Value (Child.Value 2.0))
(single_key_dict 2 . at 2.0) . should_equal 42
(single_key_dict -2 . at -2.0) . should_equal 42
(single_key_dict 'ś' . at 's\u0301') . should_equal 42
(single_key_dict 's\u0301' . at 'ś') . should_equal 42
(single_key_dict 'éabc' . at 'e\u0301abc') . should_equal 42
(single_key_dict 'e\u0301abc' . at 'éabc') . should_equal 42
(single_key_dict grand_parent_1 . at grand_parent_2) . should_equal 42
(single_key_dict (Json.parse '{"a": 1}') . at (Json.parse '{"a": 1}')) . should_equal 42
(single_key_dict (Child.Value 1) . at (Child.Value 1.0)) . should_equal 42
group_builder.specify "should support another Dictionary with NaN keys as key" <|
Dictionary.singleton (Dictionary.singleton Number.nan 1) 42 . size . should_equal 1
Dictionary.singleton (Dictionary.singleton Number.nan 1) 42 . keys . at 0 . keys . to_text . should_equal "[NaN]"
Dictionary.singleton (Dictionary.singleton Number.nan 1) 42 . keys . at 0 . get Number.nan . should_equal 1
Dictionary.singleton (Dictionary.singleton Number.nan 1) 42 . at (Dictionary.singleton Number.nan 1) . should_equal 42
group_builder.specify "should support atoms with custom comparators that violate reflexivity as keys" <|
k = My_Nan.Value "foo"
k2 = My_Nan.Value "foo"
(k==k).should_be_true
(k==k2).should_be_false
Meta.is_same_object k k2 . should_be_false
Meta.is_same_object k k . should_be_true
m = Dictionary.singleton k 10
m.contains_key k . should_be_true
m.get k . should_equal 10
m.contains_key k2 . should_be_false
m2 = m.insert k2 20
m2.get k . should_equal 10
m2.get k2 . should_equal 20
m2.size . should_equal 2
m3 = m2.insert k 30
m3.size . should_equal 2
m3.get k . should_equal 30
group_builder.specify "should support atom with custom comparators with complicated hash method" <|
keys = 0.up_to 500 . map ix->
value = ["A", "B", "C", "D", "E"].at (ix % 5)
hash_code = Comparable.from value . hash value
My_Key.Value hash_code value ix
distinct_keys = keys.fold Dictionary.empty acc_dict->
item->
acc_dict.insert item True
distinct_keys.size . should_equal 5
distinct_key_values = keys.map (_.value) . fold Dictionary.empty acc_dict->
item->
acc_dict.insert item True
distinct_key_values.size . should_equal 5
group_builder.specify "should not drop warnings from keys" <|
key = Warning.attach "my_warn" "my_key"
dict = Dictionary.singleton key 42
(Warning.get_all (dict.keys.at 0)).length . should_equal 1
group_builder.specify "should not drop warnings from values" <|
val = Warning.attach "my_warn" "my_val"
dict = Dictionary.singleton 42 val
(Warning.get_all (dict.values.at 0)).length . should_equal 1
group_builder.specify "should convert the whole Dictionary to a vector" <|
m = Dictionary.empty . insert 0 0 . insert 3 -5 . insert 1 2
m.to_vector.sort on=_.first . should_equal [[0, 0], [1, 2], [3, -5]]
group_builder.specify "should allow building the Dictionary from two vectors" <|
expected = Dictionary.empty . insert 0 0 . insert 3 -5 . insert 1 2
Dictionary.from_keys_and_values [0, 3, 1] [0, -5, 2] . should_equal expected
group_builder.specify "should allow building the Dictionary from vector like things" <|
expected = Dictionary.empty . insert 0 0 . insert 1 -5 . insert 2 2
Dictionary.from_keys_and_values (0.up_to 3) [0, -5, 2] . should_equal expected
group_builder.specify "should not allow building with duplicate keys unless explicitly allowed" <|
expected = Dictionary.empty . insert 0 0 . insert 3 -5 . insert 1 2
Dictionary.from_keys_and_values [0, 3, 1, 0] [3, -5, 2, 0] . should_fail_with Illegal_Argument
Dictionary.from_keys_and_values [0, 3, 1, 0] [3, -5, 2, 0] error_on_duplicates=False . should_equal expected
group_builder.specify "should not allow different length vectors when building" <|
Dictionary.from_keys_and_values [0, 3, 1] [3, -5, 2, 0] . should_fail_with Illegal_Argument
group_builder.specify "should allow building the Dictionary from a vector" <|
expected = Dictionary.empty . insert 0 0 . insert 3 -5 . insert 1 2
vec = [[0, 0], [3, -5], [1, 2]]
Dictionary.from_vector vec . should_equal expected
group_builder.specify "should fail when building the Dictionary from wrong vector" <|
Dictionary.from_vector [["A", 1, "B", 2]] . should_fail_with Illegal_Argument
group_builder.specify "should not allow duplicates when building the Dictionary from a vector, unless explicitly allowed" <|
vec = [[0, 0], [3, -5], [1, 2], [0, 1]]
d1 = Dictionary.from_vector vec
d1.should_fail_with Illegal_Argument
d1.catch.message . should_equal "`Dictionary.from_vector` encountered duplicate key: 0"
d2 = Dictionary.from_vector vec error_on_duplicates=False
Problems.assume_no_problems d2
d2.get 0 . should_equal 1
d2.get 3 . should_equal -5
group_builder.specify "should disallow duplicate keys when transforming the Dictionary" <|
d = Dictionary.from_vector [[1, 2], [11, 3]]
d2 = d.transform (k -> v -> [k % 10, v*2])
d2.should_fail_with Illegal_Argument
d2.catch.message . should_equal "`Dictionary.transform` encountered duplicate key: 1"
group_builder.specify "should allow mapping over values" <|
d = Dictionary.empty . insert 1 2 . insert 2 4
expected = Dictionary.empty . insert 1 4 . insert 2 8
d.map (v -> v*2) . should_equal expected
group_builder.specify "should allow mapping over keys" <|
d = Dictionary.empty . insert 1 2 . insert 2 4
expected = Dictionary.empty . insert 2 2 . insert 4 4
d.map_keys (k -> k*2) . should_equal expected
group_builder.specify "should allow mapping with keys" <|
d = Dictionary.empty . insert 1 2 . insert 2 4
expected = Dictionary.empty . insert 1 3 . insert 2 6
d.map_with_key (k -> v -> k + v) . should_equal expected
group_builder.specify "should allow iterating over each value" <|
d = Dictionary.empty . insert 1 2 . insert 2 4
expected_vec = [2, 4]
vec = Vector.build builder->
d.each (v -> builder.append v)
vec . should_equal expected_vec
group_builder.specify "should allow iterating over each key-value pair" <|
d = Dictionary.empty . insert 1 2 . insert 2 4
expected_vec = [3, 6]
vec = Vector.build builder->
d.each_with_key (k -> v -> builder.append (k+v))
vec . should_equal expected_vec
group_builder.specify "should allow folding over the values" <|
d = Dictionary.empty . insert 1 2 . insert 2 4
d.fold 0 (+) . should_equal 6
group_builder.specify "should allow folding over the key-value pairs" <|
d = Dictionary.empty . insert 1 2 . insert 2 4
d.fold_with_key 0 (l -> k -> v -> l + k + v) . should_equal 9
group_builder.specify "should be able to add a Nothing key to a Dictionary of Text" <|
m = Dictionary.empty . insert "A" 2 . insert Nothing 1 . insert "B" 3
m.at "A" . should_equal 2
m.at "B" . should_equal 3
m.at Nothing . should_equal 1
group_builder.specify "should be able to add a Nothing key to a Dictionary of Integer" <|
m = Dictionary.empty . insert 100 2 . insert Nothing 1 . insert 200 3
m.at 100 . should_equal 2
m.at 200 . should_equal 3
m.at Nothing . should_equal 1
suite_builder.group "Polyglot keys and values" group_builder->
group_builder.specify "should support polyglot keys" <|
dict = Dictionary.singleton (js_str "A") 42
dict.size.should_equal 1
dict.get "A" . should_equal 42
dict.get (js_str "A") . should_equal 42
group_builder.specify "should support host objects as keys" <|
# java.nio.path.Path has proper implementation of hashCode
dict = Dictionary.singleton (File_Utils.toPath "/home/user/file.txt") 42
dict.get "X" . should_equal Nothing
dict.get "A" . should_equal Nothing
dict.get (File_Utils.toPath "/home/user/file.txt") . should_equal 42
group_builder.specify "should support Python objects as keys" pending=pending_python_missing <|
py_obj = py_wrapper 42
dict = Dictionary.singleton py_obj "Value"
dict.get py_obj . should_equal "Value"
group_builder.specify "should support Python objects as values" pending=pending_python_missing <|
dict = Dictionary.singleton "A" (py_wrapper 42)
dict.get "A" . data . should_equal 42
group_builder.specify "should insert entries to a polyglot map" pending=pending_python_missing <|
dict = py_dict_from_vec ["A", 1, "B", 2]
dict.insert "C" 3 . keys . sort . should_equal ["A", "B", "C"]
group_builder.specify "should remove entries from a polyglot map" pending=pending_python_missing <|
dict = py_dict_from_vec ["A", 1, "B", 2]
dict.remove "B" . to_vector . should_equal [["A", 1]]
suite_builder.group "non-linear inserts" group_builder->
group_builder.specify "should handle inserts with different keys" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "B" 2
d3 = d1.insert "C" 3
d2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 1], ["C", 3]]
group_builder.specify "should handle inserts with same keys (1)" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "A" 2
d3 = d1.insert "A" 3
d4 = d1.insert "B" 4
d2.to_vector.sort on=_.first . should_equal [["A", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 3]]
d4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 4]]
group_builder.specify "should handle inserts with same keys (2)" <|
d1 = Dictionary.singleton "foo" 1
d2 = d1.insert "baz" 2
d3 = d2.insert "foo" 3
d1.to_vector.sort on=_.first . should_equal [['foo', 1]]
d2.to_vector.sort on=_.first . should_equal [['baz', 2], ['foo', 1]]
d3.to_vector.sort on=_.first . should_equal [['baz', 2], ['foo', 3]]
group_builder.specify "should handle inserts with same keys (3)" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "B" 2
d3 = d2.insert "A" 3
d4 = d2.insert "C" 4
d1.to_vector.sort on=_.first . should_equal [["A", 1]]
d2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 3], ["B", 2]]
d4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 4]]
group_builder.specify "should handle inserts with same keys (4)" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "B" 2
d3 = d2.insert "C" 3
d4 = d2.insert "D" 4
d2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
d4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["D", 4]]
group_builder.specify "should handle inserts with same keys (5)" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "B" 2
d3 = d2.insert "A" 3
d4 = d2.insert "A" 4
d2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 3], ["B", 2]]
d4.to_vector.sort on=_.first . should_equal [["A", 4], ["B", 2]]
group_builder.specify "should handle inserts with same keys (6)" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "B" 2
d3 = d2.insert "C" 3
d4 = d2.insert "A" 4
d2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
d4.to_vector.sort on=_.first . should_equal [["A", 4], ["B", 2]]
group_builder.specify "should handle inserts with same keys (7)" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "B" 2
d3 = d2.insert "C" 3
d4 = d3.insert "D" 4
d5 = d2.insert "A" 5
d2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
d4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3], ["D", 4]]
d5.to_vector.sort on=_.first . should_equal [["A", 5], ["B", 2]]
group_builder.specify "should handle inserts with same keys (8)" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "B" 2
d3 = d2.insert "C" 3
d4 = d3.insert "A" 4
d5 = d2.insert "A" 5
d2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
d4.to_vector.sort on=_.first . should_equal [["A", 4], ["B", 2], ["C", 3]]
d5.to_vector.sort on=_.first . should_equal [["A", 5], ["B", 2]]
group_builder.specify "should handle inserts with same keys (9)" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "B" 2
d3 = d2.insert "A" 3
d4 = d2.insert "B" 4
d5 = d2.insert "C" 5
d2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 3], ["B", 2]]
d4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 4]]
d5.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 5]]
group_builder.specify "should handle inserts with same keys (10)" <|
d1 = Dictionary.singleton "A" 1
d2 = d1.insert "B" 2
d3 = d2.insert "C" 3
d4 = d2.insert "D" 4
d5 = d2.insert "E" 5
d2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
d3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
d4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["D", 4]]
d5.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["E", 5]]
suite_builder.group "Polyglot hash maps" group_builder->
group_builder.specify "should pass Dictionaries as immutable maps to other languages" pending=pending_python_missing <|
dict = Dictionary.singleton "A" 1
# Python's KeyError should be raised
Test.expect_panic_with (py_update_dict dict "A" 2) Any
dict.get "A" . should_equal 1
group_builder.specify "should treat JavaScript maps as Enso Dictionaries" <|
js_dict = js_dict_from_vec ["A", 1, "B", 2]
dict = js_dict.insert "C" 3
js_dict.to_vector.should_equal [["A", 1], ["B", 2]]
dict.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
group_builder.specify "should treat Java Map as Enso Dictionary" <|
sort_by_keys vec = vec.sort by=x-> y-> Ordering.compare x.first y.first
dict = JavaMap.of "A" 1 "B" 2
(sort_by_keys dict.to_vector) . should_equal [["A", 1], ["B", 2]]
(sort_by_keys (dict.insert "C" 3 . to_vector)) . should_equal [["A", 1], ["B", 2], ["C", 3]]
group_builder.specify "should treat Python dicts as Enso Dictionaries" pending=pending_python_missing <|
py_dict = py_dict_from_vec ["A", 1, "B", 2]
dict = py_dict.insert "C" 3
py_dict.not_empty . should_be_true
py_dict.to_vector . should_contain_the_same_elements_as [["A", 1], ["B", 2]]
dict.to_vector . should_contain_the_same_elements_as [["A", 1], ["B", 2], ["C", 3]]
py_empty_dict.is_empty.should_be_true
py_empty_dict.insert "A" 1 . insert "A" 2 . get "A" . should_equal 2
group_builder.specify "should be able to remove entries" pending=pending_python_missing <|
py_dict_from_vec ["A", 1, "B", 2] . remove "A" . size . should_equal 1
py_dict_from_vec ["A", 1, "B", 2] . remove "A" . get "B" . should_equal 2
group_builder.specify "should be able to remove NaN keys" pending=pending_python_missing <|
py_dict_from_vec [Number.nan, 1] . remove Number.nan . size . should_equal 0
group_builder.specify "should pass Dictionaries with null keys to Python and back" pending=pending_python_missing <|
# Python supports None as keys, Enso support Nothing as keys
py_dict = py_dict_from_map (Dictionary.singleton Nothing 42)
py_dict.get Nothing . should_equal 42
py_dict.insert "A" 23 . get Nothing . should_equal 42
py_dict.insert Nothing 23 . get Nothing . should_equal 23
group_builder.specify "should treat Enso Dictionaries as Python dicts when passed to Python" pending=pending_python_missing <|
dict1 = Dictionary.singleton "A" 1 . insert "B" 2
py_vec_from_map dict1 . should_contain_the_same_elements_as [["A", 1], ["B", 2]]
dict2 = Dictionary.singleton "A" 1 . insert Nothing 2
py_vec_from_map dict2 . should_contain_the_same_elements_as [["A", 1], [Nothing, 2]]
add_common_specs suite_builder prefix:Text (pending : (Text | Nothing)) (empty_dict_fn : (Nothing -> Dictionary)) =
# Not on a single line - empty_dict is a method, not a variable
empty_dict =
empty_dict_fn Nothing
suite_builder.group prefix+": Common polyglot Dictionary operations" pending=pending group_builder->
group_builder.specify "should get the default comparator for polyglot maps" <|
Comparable.from empty_dict . should_equal Default_Comparator
group_builder.specify "should compare two hash maps" <|
(empty_dict.insert "a" 1).should_equal (empty_dict.insert "a" 1)
(empty_dict.insert "b" 2).should_not_equal (empty_dict.insert "a" 1)
empty_dict.should_equal empty_dict
empty_dict.should_not_equal (empty_dict.insert "a" 1)
(empty_dict.insert "a" 1 . insert "b" 2).should_equal (empty_dict.insert "b" 2 . insert "a" 1)
group_builder.specify "should allow checking for non emptiness" <|
non_empty = empty_dict . insert "foo" 1234
empty_dict.not_empty . should_be_false
non_empty.not_empty . should_be_true
group_builder.specify "should allow checking its size" <|
non_empty = empty_dict.insert "a" "b" . insert "x" "y"
empty_dict.size . should_equal 0
non_empty.size . should_equal 2
group_builder.specify "should allow checking for emptiness" <|
non_empty = empty_dict . insert "foo" 1234
empty_dict.is_empty . should_be_true
non_empty.is_empty . should_be_false
group_builder.specify "should handle incomparable values as keys" <|
empty_dict.insert Number.nan 1 . insert Number.nan 2 . get Number.nan . should_equal 2
group_builder.specify "should handle Nothing as values" <|
empty_dict.insert 1 Nothing . at 1 . should_equal Nothing
empty_dict.insert Nothing Nothing . at Nothing . should_equal Nothing
group_builder.specify "should support rewriting values with same keys" <|
dict = Dictionary.singleton "a" 1 . insert "a" 42
dict.size.should_equal 1
dict.get "a" . should_equal 42
group_builder.specify "should allow storing atoms as values" <|
json = Json.parse '{"a": 1}'
pair = Pair.new "first" "second"
dict = Dictionary.singleton 0 json . insert 1 pair
dict.get 0 . should_equal json
dict.get 1 . should_equal pair
group_builder.specify "should support NaN as keys" <|
empty_dict.insert Number.nan 1 . contains_key Number.nan . should_be_true
empty_dict.insert Number.nan 1 . values . should_equal [1]
empty_dict.insert Number.nan 1 . insert Number.nan 2 . contains_key Number.nan . should_be_true
empty_dict.insert Number.nan 1 . insert Number.nan 2 . values . should_equal [2]
empty_dict.insert Number.nan 1 . insert "key" 2 . insert Number.nan 3 . contains_key Number.nan . should_be_true
empty_dict.insert Number.nan 1 . insert "key" 2 . insert Number.nan 3 . contains_key "key" . should_be_true
empty_dict.insert Number.nan 1 . insert "key" 2 . insert Number.nan 3 . at Number.nan . should_equal 3
empty_dict.insert Number.nan 1 . insert "key" 2 . insert Number.nan 3 . at "key" . should_equal 2
empty_dict.insert Number.nan 1 . insert Number.nan Number.nan . at Number.nan . to_text . should_equal "NaN"
empty_dict.insert Number.nan 1 . insert Number.nan Number.nan . remove Number.nan . size . should_equal 0
group_builder.specify "should support arbitrary atoms as keys" <|
dict = empty_dict . insert (Pair.new "one" "two") 42
(dict.get (Pair.new "one" "two")).should_equal 42
(dict.get (Pair.new "A" "B")).should_equal Nothing
(dict.get (Pair.new "two" "two")).should_equal Nothing
group_builder.specify "should support vectors as keys" <|
dict = empty_dict . insert [1, "a", 2] "Value"
dict.size.should_equal 1
dict.get [1, "a", 2] . should_equal "Value"
group_builder.specify "should support dates as keys" <|
dict = empty_dict.insert (Date.new 1993) 1 . insert (Date.new 1993 2 5) 2 . insert (Date_Time.new 1993 2 5 13 45) 3
dict.size.should_equal 3
dict.get (Date.new 1993 6 7) . should_equal Nothing
dict.get (Date.new 1993) . should_equal 1
dict.get (Date_Time.new 1993) . should_equal Nothing
dict.get (Date.new 1993 2 5) . should_equal 2
dict.get (Date_Time.new 1993 2 5) . should_equal Nothing
dict.get (Date_Time.new 1993 2 5 13 45) . should_equal 3
group_builder.specify "should support another hash map as key" <|
keys = empty_dict.insert (Pair.new "one" "two") 42
dict = empty_dict.insert keys 23
dict.size.should_equal 1
(dict.get "A").should_equal Nothing
(dict.get keys).should_equal 23
(dict.get dict).should_equal Nothing
group_builder.specify "should handle keys with standard equality semantics" <|
dict = empty_dict.insert 2 "Hello"
(dict.get 2).should_equal "Hello"
(dict.get 2.0).should_equal "Hello"
(empty_dict.insert 2 "Hello").should_equal (empty_dict.insert 2.0 "Hello")
group_builder.specify "should handle Nothing as keys" <|
empty_dict.insert Nothing 3 . get Nothing . should_equal 3
empty_dict.insert Nothing 1 . insert Nothing 2 . get Nothing . should_equal 2
empty_dict.insert Nothing 1 . should_equal (empty_dict.insert Nothing 1)
empty_dict.insert Nothing 1 . insert Nothing 2 . at Nothing . should_equal 2
group_builder.specify "should handle JavaScript null as keys" <|
empty_dict.insert js_null 1 . at Nothing . should_equal 1
group_builder.specify "should handle Python None as keys" pending=pending_python_missing <|
empty_dict.insert py_none 1 . at Nothing . should_equal 1
group_builder.specify "should define a well-defined text conversion" <|
d = empty_dict . insert 0 0 . insert 3 -5 . insert 1 2
d.to_text . should_contain "0=0"
d.to_text . should_contain "3=-5"
d.to_text . should_contain "1=2"
group_builder.specify "should define structural equality" <|
dict_1 = empty_dict . insert "1" 2 . insert "2" "1"
dict_2 = empty_dict . insert "1" 2 . insert "2" "1"
dict_3 = empty_dict
dict_1==dict_2 . should_be_true
dict_1==dict_3 . should_be_false
dict_2==dict_3 . should_be_false
group_builder.specify "should allow inserting and looking up values" <|
m = empty_dict . insert "foo" 134 . insert "bar" 654 . insert "baz" "spam"
m.at "foo" . should_equal 134
m.at "bar" . should_equal 654
m.at "baz" . should_equal "spam"
(m.at "nope").should_fail_with No_Such_Key
group_builder.specify "should support get" <|
m = empty_dict . insert 2 3
m.get 2 0 . should_equal 3
m.get 1 10 . should_equal 10
m.get 2 (Panic.throw "missing") . should_equal 3
group_builder.specify "should allow getting a vector of the keys" <|
m = empty_dict . insert 1 2 . insert 2 4
m.keys . should_equal [1, 2]
group_builder.specify "should allow getting a vector of the values" <|
m = empty_dict . insert 1 2 . insert 2 4
m.values . should_equal [2, 4]
group_builder.specify "should support contains_key" <|
m = empty_dict . insert 2 3
m.contains_key 2 . should_be_true
m.contains_key 1 . should_be_false
group_builder.specify "should allow transforming the dictionary" <|
m = empty_dict . insert 1 2 . insert 2 4
expected = empty_dict . insert "1" 4 . insert "2" 8
m.transform (k -> v -> [k.to_text, v*2]) . should_equal expected
group_builder.specify "should be able to remove entries (1)" <|
m1 = empty_dict.insert "A" 1 . insert "B" 2
m2 = m1.remove "B"
m2.get "A" . should_equal 1
m2.remove "A" . should_equal empty_dict
m1.remove "foo" . should_fail_with No_Such_Key
group_builder.specify "should be able to remove entries (2)" <|
m1 = empty_dict.insert "A" 1
m2 = m1.insert "B" 2
m3 = m1.insert "C" 3
m2.remove "A" . to_vector . should_equal [["B", 2]]
m2.remove "B" . to_vector . should_equal [["A", 1]]
m3.remove "A" . to_vector . should_equal [["C", 3]]
m3.remove "C" . to_vector . should_equal [["A", 1]]
group_builder.specify "should be able to remove entries (3)" <|
m = empty_dict.insert "A" 1 . insert "B" 2 . insert "C" 3
m.remove "B" . should_equal (empty_dict.insert "A" 1 . insert "C" 3)
main filter=Nothing =
suite = Test.build suite_builder->
add_specs suite_builder
suite.run_with_filter filter

View File

@ -5,10 +5,11 @@ from Standard.Test import all
add_specs suite_builder =
suite_builder.group "Enso Set" group_builder->
suite_builder.group "Enso Hashset" group_builder->
group_builder.specify "should allow checking for emptiness" <|
empty_map = Set.empty
non_empty = Set.empty . insert "foo"
empty_map =
Hashset.empty
non_empty = Hashset.empty . insert "foo"
empty_map.is_empty . should_be_true
non_empty.is_empty . should_be_false
@ -16,34 +17,34 @@ add_specs suite_builder =
non_empty.not_empty . should_be_true
group_builder.specify "should be constructed from a vector" <|
s1 = Set.from_vector [1, 2, 3, 2]
s1 = Hashset.from_vector [1, 2, 3, 2]
s1.size . should_equal 3
s1.to_vector.sort . should_equal [1, 2, 3]
r2 = Set.from_vector [1, 2, 2] error_on_duplicates=True
r2 = Hashset.from_vector [1, 2, 2] error_on_duplicates=True
r2.should_fail_with Illegal_Argument
group_builder.specify "should allow checking contains" <|
s1 = Set.from_vector [1, 2, 3, 2]
s1 = Hashset.from_vector [1, 2, 3, 2]
s1.contains 1 . should_be_true
s1.contains 2 . should_be_true
s1.contains 3 . should_be_true
s1.contains 4 . should_be_false
group_builder.specify "should allow checking contains with relational NULL logic" <|
Set.from_vector [1, 2] . contains_relational 1 . should_be_true
Set.from_vector [1, 2] . contains_relational 3 . should_be_false
Set.from_vector [1, 2, Nothing] . contains_relational 1 . should_be_true
Set.from_vector [1, 2, Nothing] . contains_relational 3 . should_equal Nothing
Set.from_vector [1, 2, Nothing] . contains_relational Nothing . should_equal Nothing
Set.from_vector [1, 2] . contains_relational Nothing . should_equal Nothing
Set.from_vector [Nothing] . contains_relational Nothing . should_equal Nothing
Set.from_vector [] . contains_relational Nothing . should_be_false
Hashset.from_vector [1, 2] . contains_relational 1 . should_be_true
Hashset.from_vector [1, 2] . contains_relational 3 . should_be_false
Hashset.from_vector [1, 2, Nothing] . contains_relational 1 . should_be_true
Hashset.from_vector [1, 2, Nothing] . contains_relational 3 . should_equal Nothing
Hashset.from_vector [1, 2, Nothing] . contains_relational Nothing . should_equal Nothing
Hashset.from_vector [1, 2] . contains_relational Nothing . should_equal Nothing
Hashset.from_vector [Nothing] . contains_relational Nothing . should_equal Nothing
Hashset.from_vector [] . contains_relational Nothing . should_be_false
group_builder.specify "should allow to compute a union, intersection and difference" <|
s1 = Set.from_vector [1, 2]
s2 = Set.from_vector [2, 3]
s3 = Set.from_vector [3, 4]
s1 = Hashset.from_vector [1, 2]
s2 = Hashset.from_vector [2, 3]
s3 = Hashset.from_vector [3, 4]
(s1.union s2).to_vector.sort . should_equal [1, 2, 3]
(s1.union s3).to_vector.sort . should_equal [1, 2, 3, 4]
@ -54,19 +55,19 @@ add_specs suite_builder =
(s1.difference s1).to_vector . should_equal []
group_builder.specify "should allow to check for equality of two sets" <|
s1 = Set.from_vector [1, 2]
s2 = Set.from_vector [2, 1, 1]
s3 = Set.from_vector [1, 2, 3]
s1 = Hashset.from_vector [1, 2]
s2 = Hashset.from_vector [2, 1, 1]
s3 = Hashset.from_vector [1, 2, 3]
(s1 == s2) . should_be_true
(s1 == s1) . should_be_true
(s1 == s3) . should_be_false
group_builder.specify "should be able to convert to text" <|
s1 = Set.from_vector ["1", "2", "3"]
s2 = Set.from_vector [1, 2, 3]
s1.to_text.should_equal "Set{'1', '2', '3'}"
s2.to_text.should_equal "Set{1, 2, 3}"
s1 = Hashset.from_vector ["1", "2", "3"]
s2 = Hashset.from_vector [1, 2, 3]
s1.to_text.should_equal "Hashset{'1', '2', '3'}"
s2.to_text.should_equal "Hashset{1, 2, 3}"
main filter=Nothing =
suite = Test.build suite_builder->

View File

@ -85,13 +85,13 @@ add_specs suite_builder =
Json.parse '{"constructor": "Skew", "population": true}' . into Statistic . should_equal (Statistic.Skew True)
Json.parse '{"constructor": "NotARealOne", "population": true}' . into Statistic . should_fail_with Illegal_Argument
group_builder.specify "should be able to convert a JS_Object into a Map using into" <|
Json.parse '{"a": 15, "b": 20, "c": "X", "d": null}' . into Map . should_equal (Map.from_vector [["a", 15], ["b", 20], ["c", "X"], ["d", Nothing]])
Json.parse '{}' . into Map . should_equal Map.empty
group_builder.specify "should be able to convert a JS_Object into a Dictionary using into" <|
Json.parse '{"a": 15, "b": 20, "c": "X", "d": null}' . into Dictionary . should_equal (Dictionary.from_vector [["a", 15], ["b", 20], ["c", "X"], ["d", Nothing]])
Json.parse '{}' . into Dictionary . should_equal Dictionary.empty
# [] parses as a vector/array which does not have the `into` method, that only works for {} objects:
Test.expect_panic No_Such_Method <|
Json.parse '[]' . into Map
Json.parse '[]' . into Dictionary
group_builder.specify "should be able to deserialize Date" <|
'{"type": "Date", "constructor": "new", "year": 2018, "month": 7, "day": 3}'.should_parse_as (Date.new 2018 7 3)

View File

@ -1,637 +0,0 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Errors.No_Such_Key.No_Such_Key
from Standard.Test import all
polyglot java import java.util.Map as JavaMap
polyglot java import org.enso.base.file_system.File_Utils
## Type that violates reflexivity
type My_Nan
Value comment:Text
type My_Nan_Comparator
compare _ _ = Nothing
hash _ = 0
Comparable.from (_:My_Nan) = My_Nan_Comparator
type My_Key
Value hash_code:Integer value:Text idx:Integer
type My_Key_Comparator
# Comparison ignores idx field
compare x y =
if x.hash_code != y.hash_code then Nothing else
if x.value == y.value then Ordering.Equal else Nothing
hash x = x.hash_code
Comparable.from (_:My_Key) = My_Key_Comparator
foreign js js_str str = """
return new String(str)
foreign js js_null = """
return null
foreign js js_empty_dict = """
return new Map()
foreign python py_empty_dict = """
return {}
foreign js js_dict_from_vec vec = """
dict = new Map()
for (let i = 0; i < vec.length; i += 2) {
dict.set(vec[i], vec[i+1])
}
return dict
foreign python py_none = """
return None
foreign python py_dict_from_vec vec = """
d = {}
for i in range(0, len(vec), 2):
d[vec[i]] = vec[i + 1]
return d
foreign python py_dict_from_map map = """
d = dict()
for key in map.__iter__():
d[key] = map[key]
return d
foreign python py_vec_from_map map = """
vec = []
for key in map.__iter__():
value = map[key]
vec.append([key, value])
return vec
# Should throw error - updating immutable map from Enso
foreign python py_update_dict map key val = """
map[key] = val
foreign python py_wrapper obj = """
class MyClass:
def __init__(self, obj):
self.data = obj
return MyClass(obj)
pending_python_missing = if Polyglot.is_language_installed "python" then Nothing else "Can't run Python tests, Python is not installed."
type Child
Value data
type Parent
Value child
type GrandParent
Value parent
add_specs suite_builder =
languages = Vector.build builder->
builder . append ["Enso", _-> Map.empty, Nothing]
builder . append ["Java", _-> JavaMap.of, Nothing]
builder . append ["JavaScript", _-> js_empty_dict, Nothing]
builder . append ["Python", _-> py_empty_dict, pending_python_missing]
languages.each entry->
lang = entry.get 0
empty_map_fn = entry.get 1
pending = entry.get 2
add_common_specs suite_builder lang pending empty_map_fn
suite_builder.group "Enso maps" group_builder->
group_builder.specify "should use proper hash code for keys" <|
single_key_map key = Map.singleton key 42
grand_parent_1 = GrandParent.Value (Parent.Value (Child.Value 2))
grand_parent_2 = GrandParent.Value (Parent.Value (Child.Value 2.0))
(single_key_map 2 . at 2.0) . should_equal 42
(single_key_map -2 . at -2.0) . should_equal 42
(single_key_map 'ś' . at 's\u0301') . should_equal 42
(single_key_map 's\u0301' . at 'ś') . should_equal 42
(single_key_map 'éabc' . at 'e\u0301abc') . should_equal 42
(single_key_map 'e\u0301abc' . at 'éabc') . should_equal 42
(single_key_map grand_parent_1 . at grand_parent_2) . should_equal 42
(single_key_map (Json.parse '{"a": 1}') . at (Json.parse '{"a": 1}')) . should_equal 42
(single_key_map (Child.Value 1) . at (Child.Value 1.0)) . should_equal 42
group_builder.specify "should support another hash map with NaN keys as key" <|
Map.singleton (Map.singleton Number.nan 1) 42 . size . should_equal 1
Map.singleton (Map.singleton Number.nan 1) 42 . keys . at 0 . keys . to_text . should_equal "[NaN]"
Map.singleton (Map.singleton Number.nan 1) 42 . keys . at 0 . get Number.nan . should_equal 1
Map.singleton (Map.singleton Number.nan 1) 42 . at (Map.singleton Number.nan 1) . should_equal 42
group_builder.specify "should support atoms with custom comparators that violate reflexivity as keys" <|
k = My_Nan.Value "foo"
k2 = My_Nan.Value "foo"
(k==k).should_be_true
(k==k2).should_be_false
Meta.is_same_object k k2 . should_be_false
Meta.is_same_object k k . should_be_true
m = Map.empty.insert k 10
m.contains_key k . should_be_true
m.get k . should_equal 10
m.contains_key k2 . should_be_false
m2 = m.insert k2 20
m2.get k . should_equal 10
m2.get k2 . should_equal 20
m2.size . should_equal 2
m3 = m2.insert k 30
m3.size . should_equal 2
m3.get k . should_equal 30
group_builder.specify "should support atom with custom comparators with complicated hash method" <|
keys = 0.up_to 500 . map ix->
value = ["A", "B", "C", "D", "E"].at (ix % 5)
hash_code = Comparable.from value . hash value
My_Key.Value hash_code value ix
distinct_keys = keys.fold Map.empty acc_map->
item->
acc_map.insert item True
distinct_keys.size . should_equal 5
distinct_key_values = keys.map (_.value) . fold Map.empty acc_map->
item->
acc_map.insert item True
distinct_key_values.size . should_equal 5
group_builder.specify "should not drop warnings from keys" <|
key = Warning.attach "my_warn" "my_key"
map = Map.singleton key 42
(Warning.get_all (map.keys.at 0)).length . should_equal 1
group_builder.specify "should not drop warnings from values" <|
val = Warning.attach "my_warn" "my_val"
map = Map.singleton 42 val
(Warning.get_all (map.values.at 0)).length . should_equal 1
group_builder.specify "should convert the whole map to a vector" <|
m = Map.empty . insert 0 0 . insert 3 -5 . insert 1 2
m.to_vector.sort on=_.first . should_equal [[0, 0], [1, 2], [3, -5]]
group_builder.specify "should allow building the map from two vectors" <|
expected = Map.empty . insert 0 0 . insert 3 -5 . insert 1 2
Map.from_keys_and_values [0, 3, 1] [0, -5, 2] . should_equal expected
group_builder.specify "should allow building the map from vector like things" <|
expected = Map.empty . insert 0 0 . insert 1 -5 . insert 2 2
Map.from_keys_and_values (0.up_to 3) [0, -5, 2] . should_equal expected
group_builder.specify "should not allow building with duplicate keys unless explicitly allowed" <|
expected = Map.empty . insert 0 0 . insert 3 -5 . insert 1 2
Map.from_keys_and_values [0, 3, 1, 0] [3, -5, 2, 0] . should_fail_with Illegal_Argument
Map.from_keys_and_values [0, 3, 1, 0] [3, -5, 2, 0] error_on_duplicates=False . should_equal expected
group_builder.specify "should not allow different length vectors when building" <|
Map.from_keys_and_values [0, 3, 1] [3, -5, 2, 0] . should_fail_with Illegal_Argument
group_builder.specify "should allow building the map from a vector" <|
expected = Map.empty . insert 0 0 . insert 3 -5 . insert 1 2
vec = [[0, 0], [3, -5], [1, 2]]
Map.from_vector vec . should_equal expected
group_builder.specify "should fail when building the map from wrong vector" <|
Map.from_vector [["A", 1, "B", 2]] . should_fail_with Illegal_Argument
group_builder.specify "should not allow duplicates when building the map from a vector, unless explicitly allowed" <|
vec = [[0, 0], [3, -5], [1, 2], [0, 1]]
m1 = Map.from_vector vec
m1.should_fail_with Illegal_Argument
m1.catch.message . should_equal "`Map.from_vector` encountered duplicate key: 0"
m2 = Map.from_vector vec error_on_duplicates=False
Problems.assume_no_problems m2
m2.get 0 . should_equal 1
m2.get 3 . should_equal -5
group_builder.specify "should disallow duplicate keys when transforming the map" <|
m = Map.from_vector [[1, 2], [11, 3]]
m2 = m.transform (k -> v -> [k % 10, v*2])
m2.should_fail_with Illegal_Argument
m2.catch.message . should_equal "`Map.transform` encountered duplicate key: 1"
group_builder.specify "should allow mapping over values" <|
m = Map.empty . insert 1 2 . insert 2 4
expected = Map.empty . insert 1 4 . insert 2 8
m.map (v -> v*2) . should_equal expected
group_builder.specify "should allow mapping over keys" <|
m = Map.empty . insert 1 2 . insert 2 4
expected = Map.empty . insert 2 2 . insert 4 4
m.map_keys (k -> k*2) . should_equal expected
group_builder.specify "should allow mapping with keys" <|
m = Map.empty . insert 1 2 . insert 2 4
expected = Map.empty . insert 1 3 . insert 2 6
m.map_with_key (k -> v -> k + v) . should_equal expected
group_builder.specify "should allow iterating over each value" <|
m = Map.empty . insert 1 2 . insert 2 4
expected_vec = [2, 4]
vec = Vector.build builder->
m.each (v -> builder.append v)
vec . should_equal expected_vec
group_builder.specify "should allow iterating over each key-value pair" <|
m = Map.empty . insert 1 2 . insert 2 4
expected_vec = [3, 6]
vec = Vector.build builder->
m.each_with_key (k -> v -> builder.append (k+v))
vec . should_equal expected_vec
group_builder.specify "should allow folding over the values" <|
m = Map.empty . insert 1 2 . insert 2 4
m.fold 0 (+) . should_equal 6
group_builder.specify "should allow folding over the key-value pairs" <|
m = Map.empty . insert 1 2 . insert 2 4
m.fold_with_key 0 (l -> k -> v -> l + k + v) . should_equal 9
group_builder.specify "should be able to add a Nothing key to the map of Text" <|
m = Map.empty . insert "A" 2 . insert Nothing 1 . insert "B" 3
m.at "A" . should_equal 2
m.at "B" . should_equal 3
m.at Nothing . should_equal 1
group_builder.specify "should be able to add a Nothing key to the map of Integer" <|
m = Map.empty . insert 100 2 . insert Nothing 1 . insert 200 3
m.at 100 . should_equal 2
m.at 200 . should_equal 3
m.at Nothing . should_equal 1
suite_builder.group "Polyglot keys and values" group_builder->
group_builder.specify "should support polyglot keys" <|
map = Map.singleton (js_str "A") 42
map.size.should_equal 1
map.get "A" . should_equal 42
map.get (js_str "A") . should_equal 42
group_builder.specify "should support host objects as keys" <|
# java.nio.path.Path has proper implementation of hashCode
map = Map.singleton (File_Utils.toPath "/home/user/file.txt") 42
map.get "X" . should_equal Nothing
map.get "A" . should_equal Nothing
map.get (File_Utils.toPath "/home/user/file.txt") . should_equal 42
group_builder.specify "should support Python objects as keys" pending=pending_python_missing <|
py_obj = py_wrapper 42
map = Map.singleton py_obj "Value"
map.get py_obj . should_equal "Value"
group_builder.specify "should support Python objects as values" pending=pending_python_missing <|
map = Map.singleton "A" (py_wrapper 42)
map.get "A" . data . should_equal 42
group_builder.specify "should insert entries to a polyglot map" pending=pending_python_missing <|
dict = py_dict_from_vec ["A", 1, "B", 2]
dict.insert "C" 3 . keys . sort . should_equal ["A", "B", "C"]
group_builder.specify "should remove entries from a polyglot map" pending=pending_python_missing <|
dict = py_dict_from_vec ["A", 1, "B", 2]
dict.remove "B" . to_vector . should_equal [["A", 1]]
suite_builder.group "non-linear inserts" group_builder->
group_builder.specify "should handle inserts with different keys" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "B" 2
m3 = m1.insert "C" 3
m2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 1], ["C", 3]]
group_builder.specify "should handle inserts with same keys (1)" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "A" 2
m3 = m1.insert "A" 3
m4 = m1.insert "B" 4
m2.to_vector.sort on=_.first . should_equal [["A", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 3]]
m4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 4]]
group_builder.specify "should handle inserts with same keys (2)" <|
m1 = Map.singleton "foo" 1
m2 = m1.insert "baz" 2
m3 = m2.insert "foo" 3
m1.to_vector.sort on=_.first . should_equal [['foo', 1]]
m2.to_vector.sort on=_.first . should_equal [['baz', 2], ['foo', 1]]
m3.to_vector.sort on=_.first . should_equal [['baz', 2], ['foo', 3]]
group_builder.specify "should handle inserts with same keys (3)" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "B" 2
m3 = m2.insert "A" 3
m4 = m2.insert "C" 4
m1.to_vector.sort on=_.first . should_equal [["A", 1]]
m2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 3], ["B", 2]]
m4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 4]]
group_builder.specify "should handle inserts with same keys (4)" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "B" 2
m3 = m2.insert "C" 3
m4 = m2.insert "D" 4
m2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
m4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["D", 4]]
group_builder.specify "should handle inserts with same keys (5)" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "B" 2
m3 = m2.insert "A" 3
m4 = m2.insert "A" 4
m2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 3], ["B", 2]]
m4.to_vector.sort on=_.first . should_equal [["A", 4], ["B", 2]]
group_builder.specify "should handle inserts with same keys (6)" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "B" 2
m3 = m2.insert "C" 3
m4 = m2.insert "A" 4
m2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
m4.to_vector.sort on=_.first . should_equal [["A", 4], ["B", 2]]
group_builder.specify "should handle inserts with same keys (7)" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "B" 2
m3 = m2.insert "C" 3
m4 = m3.insert "D" 4
m5 = m2.insert "A" 5
m2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
m4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3], ["D", 4]]
m5.to_vector.sort on=_.first . should_equal [["A", 5], ["B", 2]]
group_builder.specify "should handle inserts with same keys (8)" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "B" 2
m3 = m2.insert "C" 3
m4 = m3.insert "A" 4
m5 = m2.insert "A" 5
m2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
m4.to_vector.sort on=_.first . should_equal [["A", 4], ["B", 2], ["C", 3]]
m5.to_vector.sort on=_.first . should_equal [["A", 5], ["B", 2]]
group_builder.specify "should handle inserts with same keys (9)" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "B" 2
m3 = m2.insert "A" 3
m4 = m2.insert "B" 4
m5 = m2.insert "C" 5
m2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 3], ["B", 2]]
m4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 4]]
m5.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 5]]
group_builder.specify "should handle inserts with same keys (10)" <|
m1 = Map.singleton "A" 1
m2 = m1.insert "B" 2
m3 = m2.insert "C" 3
m4 = m2.insert "D" 4
m5 = m2.insert "E" 5
m2.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2]]
m3.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
m4.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["D", 4]]
m5.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["E", 5]]
suite_builder.group "Polyglot hash maps" group_builder->
group_builder.specify "should pass maps as immutable maps to other langs" pending=pending_python_missing <|
map = Map.singleton "A" 1
# Python's KeyError should be raised
Test.expect_panic_with (py_update_dict map "A" 2) Any
map.get "A" . should_equal 1
group_builder.specify "should treat JavaScript maps as Enso maps" <|
js_dict = js_dict_from_vec ["A", 1, "B", 2]
map = js_dict.insert "C" 3
js_dict.to_vector.should_equal [["A", 1], ["B", 2]]
map.to_vector.sort on=_.first . should_equal [["A", 1], ["B", 2], ["C", 3]]
group_builder.specify "should treat Java Map as Enso map" <|
sort_by_keys vec = vec.sort by=x-> y-> Ordering.compare x.first y.first
jmap = JavaMap.of "A" 1 "B" 2
(sort_by_keys jmap.to_vector) . should_equal [["A", 1], ["B", 2]]
(sort_by_keys (jmap.insert "C" 3 . to_vector)) . should_equal [["A", 1], ["B", 2], ["C", 3]]
group_builder.specify "should treat Python dicts as Enso maps" pending=pending_python_missing <|
py_dict = py_dict_from_vec ["A", 1, "B", 2]
map = py_dict.insert "C" 3
py_dict.not_empty . should_be_true
py_dict.to_vector . should_contain_the_same_elements_as [["A", 1], ["B", 2]]
map.to_vector . should_contain_the_same_elements_as [["A", 1], ["B", 2], ["C", 3]]
py_empty_dict.is_empty.should_be_true
py_empty_dict.insert "A" 1 . insert "A" 2 . get "A" . should_equal 2
group_builder.specify "should be able to remove entries" pending=pending_python_missing <|
py_dict_from_vec ["A", 1, "B", 2] . remove "A" . size . should_equal 1
py_dict_from_vec ["A", 1, "B", 2] . remove "A" . get "B" . should_equal 2
group_builder.specify "should be able to remove NaN keys" pending=pending_python_missing <|
py_dict_from_vec [Number.nan, 1] . remove Number.nan . size . should_equal 0
group_builder.specify "should pass maps with null keys to Python and back" pending=pending_python_missing <|
# Python supports None as keys, Enso support Nothing as keys
py_dict = py_dict_from_map (Map.singleton Nothing 42)
py_dict.get Nothing . should_equal 42
py_dict.insert "A" 23 . get Nothing . should_equal 42
py_dict.insert Nothing 23 . get Nothing . should_equal 23
group_builder.specify "should treat Enso maps as Python dicts when passed to Python" pending=pending_python_missing <|
map1 = Map.empty.insert "A" 1 . insert "B" 2
py_vec_from_map map1 . should_contain_the_same_elements_as [["A", 1], ["B", 2]]
map2 = Map.empty.insert "A" 1 . insert Nothing 2
py_vec_from_map map2 . should_contain_the_same_elements_as [["A", 1], [Nothing, 2]]
add_common_specs suite_builder prefix:Text (pending : (Text | Nothing)) (empty_map_fn : (Nothing -> Map)) =
# Not on a single line - empty_map is a method, not a variable
empty_map =
empty_map_fn Nothing
suite_builder.group prefix+": Common polyglot Map operations" pending=pending group_builder->
group_builder.specify "should get the default comparator for polyglot maps" <|
Comparable.from empty_map . should_equal Default_Comparator
group_builder.specify "should compare two hash maps" <|
(empty_map.insert "a" 1).should_equal (empty_map.insert "a" 1)
(empty_map.insert "b" 2).should_not_equal (empty_map.insert "a" 1)
empty_map.should_equal empty_map
empty_map.should_not_equal (empty_map.insert "a" 1)
(empty_map.insert "a" 1 . insert "b" 2).should_equal (empty_map.insert "b" 2 . insert "a" 1)
group_builder.specify "should allow checking for non emptiness" <|
non_empty = empty_map . insert "foo" 1234
empty_map.not_empty . should_be_false
non_empty.not_empty . should_be_true
group_builder.specify "should allow checking its size" <|
non_empty = empty_map.insert "a" "b" . insert "x" "y"
empty_map.size . should_equal 0
non_empty.size . should_equal 2
group_builder.specify "should allow checking for emptiness" <|
non_empty = empty_map . insert "foo" 1234
empty_map.is_empty . should_be_true
non_empty.is_empty . should_be_false
group_builder.specify "should handle incomparable values as keys" <|
empty_map.insert Number.nan 1 . insert Number.nan 2 . get Number.nan . should_equal 2
group_builder.specify "should handle Nothing as values" <|
empty_map.insert 1 Nothing . at 1 . should_equal Nothing
empty_map.insert Nothing Nothing . at Nothing . should_equal Nothing
group_builder.specify "should support rewriting values with same keys" <|
map = Map.empty.insert "a" 1 . insert "a" 42
map.size.should_equal 1
map.get "a" . should_equal 42
group_builder.specify "should allow storing atoms as values" <|
json = Json.parse '{"a": 1}'
pair = Pair.new "first" "second"
map = Map.empty.insert 0 json . insert 1 pair
map.get 0 . should_equal json
map.get 1 . should_equal pair
group_builder.specify "should support NaN as keys" <|
empty_map.insert Number.nan 1 . contains_key Number.nan . should_be_true
empty_map.insert Number.nan 1 . values . should_equal [1]
empty_map.insert Number.nan 1 . insert Number.nan 2 . contains_key Number.nan . should_be_true
empty_map.insert Number.nan 1 . insert Number.nan 2 . values . should_equal [2]
empty_map.insert Number.nan 1 . insert "key" 2 . insert Number.nan 3 . contains_key Number.nan . should_be_true
empty_map.insert Number.nan 1 . insert "key" 2 . insert Number.nan 3 . contains_key "key" . should_be_true
empty_map.insert Number.nan 1 . insert "key" 2 . insert Number.nan 3 . at Number.nan . should_equal 3
empty_map.insert Number.nan 1 . insert "key" 2 . insert Number.nan 3 . at "key" . should_equal 2
empty_map.insert Number.nan 1 . insert Number.nan Number.nan . at Number.nan . to_text . should_equal "NaN"
empty_map.insert Number.nan 1 . insert Number.nan Number.nan . remove Number.nan . size . should_equal 0
group_builder.specify "should support arbitrary atoms as keys" <|
map = empty_map . insert (Pair.new "one" "two") 42
(map.get (Pair.new "one" "two")).should_equal 42
(map.get (Pair.new "A" "B")).should_equal Nothing
(map.get (Pair.new "two" "two")).should_equal Nothing
group_builder.specify "should support vectors as keys" <|
map = empty_map . insert [1, "a", 2] "Value"
map.size.should_equal 1
map.get [1, "a", 2] . should_equal "Value"
group_builder.specify "should support dates as keys" <|
map = empty_map.insert (Date.new 1993) 1 . insert (Date.new 1993 2 5) 2 . insert (Date_Time.new 1993 2 5 13 45) 3
map.size.should_equal 3
map.get (Date.new 1993 6 7) . should_equal Nothing
map.get (Date.new 1993) . should_equal 1
map.get (Date_Time.new 1993) . should_equal Nothing
map.get (Date.new 1993 2 5) . should_equal 2
map.get (Date_Time.new 1993 2 5) . should_equal Nothing
map.get (Date_Time.new 1993 2 5 13 45) . should_equal 3
group_builder.specify "should support another hash map as key" <|
key_map = empty_map.insert (Pair.new "one" "two") 42
map = empty_map.insert key_map 23
map.size.should_equal 1
(map.get "A").should_equal Nothing
(map.get key_map).should_equal 23
(map.get map).should_equal Nothing
group_builder.specify "should handle keys with standard equality semantics" <|
map = empty_map.insert 2 "Hello"
(map.get 2).should_equal "Hello"
(map.get 2.0).should_equal "Hello"
(empty_map.insert 2 "Hello").should_equal (empty_map.insert 2.0 "Hello")
group_builder.specify "should handle Nothing as keys" <|
empty_map.insert Nothing 3 . get Nothing . should_equal 3
empty_map.insert Nothing 1 . insert Nothing 2 . get Nothing . should_equal 2
empty_map.insert Nothing 1 . should_equal (empty_map.insert Nothing 1)
empty_map.insert Nothing 1 . insert Nothing 2 . at Nothing . should_equal 2
group_builder.specify "should handle JavaScript null as keys" <|
empty_map.insert js_null 1 . at Nothing . should_equal 1
group_builder.specify "should handle Python None as keys" pending=pending_python_missing <|
empty_map.insert py_none 1 . at Nothing . should_equal 1
group_builder.specify "should define a well-defined text conversion" <|
m = empty_map . insert 0 0 . insert 3 -5 . insert 1 2
m.to_text . should_contain "0=0"
m.to_text . should_contain "3=-5"
m.to_text . should_contain "1=2"
group_builder.specify "should define structural equality" <|
map_1 = empty_map . insert "1" 2 . insert "2" "1"
map_2 = empty_map . insert "1" 2 . insert "2" "1"
map_3 = empty_map
map_1==map_2 . should_be_true
map_1==map_3 . should_be_false
map_2==map_3 . should_be_false
group_builder.specify "should allow inserting and looking up values" <|
m = empty_map . insert "foo" 134 . insert "bar" 654 . insert "baz" "spam"
m.at "foo" . should_equal 134
m.at "bar" . should_equal 654
m.at "baz" . should_equal "spam"
(m.at "nope").should_fail_with No_Such_Key
group_builder.specify "should support get" <|
m = empty_map . insert 2 3
m.get 2 0 . should_equal 3
m.get 1 10 . should_equal 10
m.get 2 (Panic.throw "missing") . should_equal 3
group_builder.specify "should allow getting a vector of the keys" <|
m = empty_map . insert 1 2 . insert 2 4
m.keys . should_equal [1, 2]
group_builder.specify "should allow getting a vector of the values" <|
m = empty_map . insert 1 2 . insert 2 4
m.values . should_equal [2, 4]
group_builder.specify "should support contains_key" <|
m = empty_map . insert 2 3
m.contains_key 2 . should_be_true
m.contains_key 1 . should_be_false
group_builder.specify "should allow transforming the map" <|
m = empty_map . insert 1 2 . insert 2 4
expected = empty_map . insert "1" 4 . insert "2" 8
m.transform (k -> v -> [k.to_text, v*2]) . should_equal expected
group_builder.specify "should be able to remove entries (1)" <|
m1 = empty_map.insert "A" 1 . insert "B" 2
m2 = m1.remove "B"
m2.get "A" . should_equal 1
m2.remove "A" . should_equal empty_map
m1.remove "foo" . should_fail_with No_Such_Key
group_builder.specify "should be able to remove entries (2)" <|
m1 = empty_map.insert "A" 1
m2 = m1.insert "B" 2
m3 = m1.insert "C" 3
m2.remove "A" . to_vector . should_equal [["B", 2]]
m2.remove "B" . to_vector . should_equal [["A", 1]]
m3.remove "A" . to_vector . should_equal [["C", 3]]
m3.remove "C" . to_vector . should_equal [["A", 1]]
group_builder.specify "should be able to remove entries (3)" <|
m = empty_map.insert "A" 1 . insert "B" 2 . insert "C" 3
m.remove "B" . should_equal (empty_map.insert "A" 1 . insert "C" 3)
main filter=Nothing =
suite = Test.build suite_builder->
add_specs suite_builder
suite.run_with_filter filter

View File

@ -393,7 +393,7 @@ add_specs suite_builder =
group_builder.specify "should provide access to info about group names" <|
data.pattern.named_groups.sort . should_equal ["empty", "letters"]
data.pattern.group_nums_to_names . should_equal <| Map.from_vector [[2, "letters"],[4, "empty"]]
data.pattern.group_nums_to_names . should_equal <| Dictionary.from_vector [[2, "letters"],[4, "empty"]]
group_builder.specify "should return the results of all named groups" <|
groups = data.match.named_groups

View File

@ -133,8 +133,8 @@ add_specs suite_builder =
data.root.at 3 . attribute "does_not_exist" if_missing="if_missing" . should_equal "if_missing"
group_builder.specify "Can get element an attribute map" <|
data.root.at 2 . attributes . should_equal (Map.from_vector [["studentId", "1000"], ["year", "2"]])
data.root.at 3 . attributes . should_equal (Map.from_vector [["studentId", "1001"], ["year", "3"]])
data.root.at 2 . attributes . should_equal (Dictionary.from_vector [["studentId", "1000"], ["year", "2"]])
data.root.at 3 . attributes . should_equal (Dictionary.from_vector [["studentId", "1001"], ["year", "3"]])
group_builder.specify "Can get nodes via xpath" <|
classes = data.root.get_xpath "/class"

View File

@ -30,12 +30,13 @@ import project.Data.Array_Proxy_Spec
import project.Data.Bool_Spec
import project.Data.Base_64_Spec
import project.Data.Decimal_Spec
import project.Data.Dictionary_Spec
import project.Data.Function_Spec
import project.Data.Hashset_Spec
import project.Data.Interval_Spec
import project.Data.Json_Spec
import project.Data.List_Spec
import project.Data.Locale_Spec
import project.Data.Map_Spec
import project.Data.Maybe_Spec
import project.Data.Numbers_Spec
import project.Data.Ordering_Spec
@ -47,7 +48,6 @@ import project.Data.Polyglot_Spec
import project.Data.Problems_Spec
import project.Data.Range_Spec
import project.Data.Regression_Spec
import project.Data.Set_Spec
import project.Data.Statistics_Spec
import project.Data.Time.Spec as Time_Spec
import project.Data.Vector_Spec
@ -129,8 +129,8 @@ main filter=Nothing =
Json_Spec.add_specs suite_builder
List_Spec.add_specs suite_builder
Locale_Spec.add_specs suite_builder
Map_Spec.add_specs suite_builder
Set_Spec.add_specs suite_builder
Dictionary_Spec.add_specs suite_builder
Hashset_Spec.add_specs suite_builder
Maybe_Spec.add_specs suite_builder
Meta_Spec.add_specs suite_builder
Instrumentor_Spec.add_specs suite_builder

View File

@ -42,7 +42,7 @@ add_specs suite_builder =
req.body.should_equal (Request_Body.Json json)
req.headers.should_equal [Header.application_json]
group_builder.specify "should set form body" <|
body_form = Map.from_vector [["key", "val"]]
body_form = Dictionary.from_vector [["key", "val"]]
req = Request.get test_uri . with_form body_form
req.body.should_equal (Request_Body.Form_Data body_form)
req.headers.should_equal [Header.application_x_www_form_urlencoded]

View File

@ -288,14 +288,14 @@ add_specs suite_builder =
group_builder.specify "Can perform a url-encoded form POST" <| Test.with_retries <|
test_file = enso_project.data / "sample.txt"
form_data = Map.from_vector [["key", "val"], ["a_file", test_file]]
form_data = Dictionary.from_vector [["key", "val"], ["a_file", test_file]]
response = Data.post url_post (Request_Body.Form_Data form_data url_encoded=True)
response.at "headers" . at "Content-Type" . should_equal "application/x-www-form-urlencoded"
response.at "data" . replace "%0D%" "%" . should_equal 'key=val&a_file=Cupcake+ipsum+dolor+sit+amet.+Caramels+tootsie+roll+cake+ice+cream.+Carrot+cake+apple+pie+gingerbread+chocolate+cake+pudding+tart+souffl%C3%A9+jelly+beans+gummies.%0A%0ATootsie+roll+chupa+chups+muffin+croissant+fruitcake+jujubes+danish+cotton+candy+danish.+Oat+cake+chocolate+fruitcake+halvah+icing+oat+cake+toffee+powder.+Pastry+drag%C3%A9e+croissant.+Ice+cream+candy+canes+dessert+muffin+sugar+plum+tart+jujubes.%0A'
group_builder.specify "Can perform a multipart form POST" <| Test.with_retries <|
test_file = enso_project.data / "sample.png"
form_data = Map.from_vector [["key", "val"], ["a_file", test_file]]
form_data = Dictionary.from_vector [["key", "val"], ["a_file", test_file]]
response = Data.post url_post (Request_Body.Form_Data form_data)
response_json = response
response_json.at "headers" . at "Content-Type" . should_start_with "multipart/form-data; boundary="

View File

@ -20,7 +20,7 @@ sum_recur n = if n == 0 then 0 else 1 + sum_recur n-1
build_map size =
rand = Java_Random.new
0.up_to size . fold Map.empty (m -> i -> m.insert (rand.nextInt 10000) i)
0.up_to size . fold Dictionary.empty (m -> i -> m.insert (rand.nextInt 10000) i)
type Data
Value ~list ~vec ~vec_float

View File

@ -44,13 +44,13 @@ collect_benches = Bench.build builder->
builder.group ("Enso_Hash_Map_" + n.to_text) options group_builder->
# Scenario similar to what is done in distinct
group_builder.specify "Enso_Incremental" <|
Scenario.Instance (_ -> Map.empty) . run_distinct data.ints
Scenario.Instance (_ -> Dictionary.empty) . run_distinct data.ints
group_builder.specify "Java_Incremental" <|
Scenario.Instance (_ -> JavaHashMapWrapper.new) . run_distinct data.ints
# A scenario similar to what is done in add_row_number with grouping
group_builder.specify "Enso_Replacement" <|
Scenario.Instance (_ -> Map.empty) . run_count_keys data.ints
Scenario.Instance (_ -> Dictionary.empty) . run_count_keys data.ints
group_builder.specify "Java_Replacement" <|
Scenario.Instance (_ -> JavaHashMapWrapper.new) . run_count_keys data.ints

View File

@ -49,8 +49,8 @@ add_specs suite_builder = suite_builder.group "Examples" group_builder->
group_builder.specify "should provide a basic cons list" <|
Examples.list.length . should_equal 3
group_builder.specify "should provide a basic KV map" <|
Examples.map.size . should_equal 3
group_builder.specify "should provide a basic KV dictionary" <|
Examples.dictionary.size . should_equal 3
group_builder.specify "should provide a type with no methods" <|
Examples.No_Methods.should_be_a Examples.No_Methods

View File

@ -583,14 +583,14 @@ add_snowflake_specs suite_builder create_connection_fn db_name =
Common_Table_Operations.Main.add_specs suite_builder setup
## PRIVATE
supported_replace_params : Set Replace_Params
supported_replace_params : Hashset Replace_Params
supported_replace_params =
e0 = [Replace_Params.Value Text Case_Sensitivity.Default False, Replace_Params.Value Text Case_Sensitivity.Default True, Replace_Params.Value Text Case_Sensitivity.Sensitive False]
e1 = [Replace_Params.Value Text Case_Sensitivity.Sensitive True, Replace_Params.Value Text Case_Sensitivity.Insensitive False, Replace_Params.Value Text Case_Sensitivity.Insensitive True]
e2 = [Replace_Params.Value Regex Case_Sensitivity.Default False, Replace_Params.Value Regex Case_Sensitivity.Default True, Replace_Params.Value Regex Case_Sensitivity.Sensitive False]
e3 = [Replace_Params.Value Regex Case_Sensitivity.Sensitive True, Replace_Params.Value Regex Case_Sensitivity.Insensitive False, Replace_Params.Value Regex Case_Sensitivity.Insensitive True]
e4 = [Replace_Params.Value DB_Column Case_Sensitivity.Default False, Replace_Params.Value DB_Column Case_Sensitivity.Sensitive False]
Set.from_vector <| e0 + e1 + e2 + e3 + e4
Hashset.from_vector <| e0 + e1 + e2 + e3 + e4
add_table_specs suite_builder =
case create_connection_builder of

View File

@ -1297,7 +1297,7 @@ add_specs suite_builder setup =
input_type = Meta.type_of term
params = Replace_Params.Value input_type case_sensitivity only_first
supported_replace_params = setup.test_selection.supported_replace_params
supported_replace_params . should_be_a Set
supported_replace_params . should_be_a Hashset
are_params_supported = supported_replace_params.contains params
case are_params_supported of
True -> column.text_replace term new_text case_sensitivity only_first . to_vector . should_equal expected

View File

@ -186,7 +186,7 @@ add_specs suite_builder setup =
problems = [Duplicate_Output_Column_Names.Error ["x Agg1", "y Agg1", "z Agg1"]]
Problems.test_problem_handling action problems tester
table3 = data.table2.rename_columns (Map.from_vector [["Group", "x"]])
table3 = data.table2.rename_columns (Dictionary.from_vector [["Group", "x"]])
action3 = table3.cross_tab ["x"] "Key" on_problems=_
tester3 table =
table.column_names . should_equal ["x", "x 1", "y", "z"]

View File

@ -56,7 +56,7 @@ add_specs suite_builder setup =
group_builder.specify "should be able to replace values via a lookup table provided as a Map" <|
table = table_builder [['x', [1, 2, 3, 4, 2]], ['y', ['a', 'b', 'c', 'd', 'e']]]
lookup_table = Map.from_vector [[2, 20], [1, 10], [4, 40], [3, 30]]
lookup_table = Dictionary.from_vector [[2, 20], [1, 10], [4, 40], [3, 30]]
expected = table_builder [['x', [10, 20, 20, 30, 40]], ['y', ['a', 'b', 'e', 'c', 'd']]]
result = table.replace lookup_table 'x' . sort ["x", "y"]
result . should_equal expected
@ -158,25 +158,25 @@ add_specs suite_builder setup =
group_builder.specify "should accept an empty lookup map, if allow_unmatched_rows=True, but expect a warning" <|
table = table_builder [['x', [1, 2, 3, 4, 2]], ['y', ['a', 'b', 'c', 'd', 'e']]]
t = table.replace Map.empty 'x'
t = table.replace Dictionary.empty 'x'
t . should_equal table
Problems.expect_warning (Empty_Error.Error "lookup_table") t
group_builder.specify "should throw an error on an empty lookup map and non-empty base table if allow_unmatched_rows=False" <|
table = table_builder [['x', [1, 2, 3, 4, 2]], ['y', ['a', 'b', 'c', 'd', 'e']]] . sort ['x']
t = table.replace Map.empty 'x' allow_unmatched_rows=False
t = table.replace Dictionary.empty 'x' allow_unmatched_rows=False
t . should_fail_with Unmatched_Rows_In_Lookup
t.catch.example_key_values . should_equal [1]
group_builder.specify "should accept an empty lookup map if the base table is also empty, but expect a warning" <|
table = table_builder_typed [['x', []], ['z', []]] Value_Type.Integer
t = table.replace Map.empty 'x'
t = table.replace Dictionary.empty 'x'
t . should_equal table
Problems.expect_warning (Empty_Error.Error "lookup_table") t
group_builder.specify "should not allow from/to_coumn to specified if the argument is a Map" <|
table = table_builder [['x', [1, 2, 3, 4, 2]], ['y', ['a', 'b', 'c', 'd', 'e']]]
lookup_table = Map.from_vector [[2, 20], [1, 10], [4, 40], [3, 30]]
lookup_table = Dictionary.from_vector [[2, 20], [1, 10], [4, 40], [3, 30]]
table.replace lookup_table 'x' from_column=8 . should_fail_with Illegal_Argument
table.replace lookup_table 'x' to_column=9 . should_fail_with Illegal_Argument
table.replace lookup_table 'x' from_column=8 to_column=9 . should_fail_with Illegal_Argument

View File

@ -28,7 +28,7 @@ type Data
add_specs suite_builder setup =
prefix = setup.prefix
create_connection_fn = setup.create_connection_func
suite_builder.group prefix+"Table.make_table_from_map/vectors" group_builder->
suite_builder.group prefix+"Table.make_table_from_dictionary/vectors" group_builder->
data = Data.setup setup create_connection_fn
group_builder.teardown <|
@ -66,21 +66,21 @@ add_specs suite_builder setup =
vecs2 = [[], [3, 4, 5], [6, 7, 8]]
data.dummy_table.make_table_from_vectors vecs2 ['x', 'y', 'z'] . read . should_fail_with Illegal_Argument
group_builder.specify "should be able to create a literal table from a map" <|
map = Map.from_vector [['x', 1], ['y', 2], ['z', 3]]
t = data.dummy_table.make_table_from_map map 'k' 'v' . sort 'v'
group_builder.specify "should be able to create a literal table from a dictionary" <|
map = Dictionary.from_vector [['x', 1], ['y', 2], ['z', 3]]
t = data.dummy_table.make_table_from_dictionary map 'k' 'v' . sort 'v'
t.at 'k' . to_vector . should_equal ['x', 'y', 'z']
t.at 'v' . to_vector . should_equal [1, 2, 3]
if setup.is_database then
group_builder.specify "should not be able to create a literal table from an empty map" <|
map = Map.empty
data.dummy_table.make_table_from_map map 'k' 'v' . should_fail_with Illegal_Argument
group_builder.specify "should not be able to create a literal table from an empty dictionary" <|
map = Dictionary.empty
data.dummy_table.make_table_from_dictionary map 'k' 'v' . should_fail_with Illegal_Argument
if setup.is_database.not then
group_builder.specify "should be able to create a literal table from an empty map" <|
map = Map.empty
t = data.dummy_table.make_table_from_map map 'k' 'v'
group_builder.specify "should be able to create a literal table from an empty dictionary" <|
map = Dictionary.empty
t = data.dummy_table.make_table_from_dictionary map 'k' 'v'
t.row_count . should_equal 0
if setup.is_database then

View File

@ -481,10 +481,10 @@ add_specs suite_builder setup =
t1 = table_builder [["alpha", [1]], ["name=123", [2]], ["name= foo bar", [3]]]
expect_column_names ["alpha", "key:123", "key: foo bar"] <|
t1.rename_columns (Map.from_vector [["name=(.*)".to_regex, "key:$1"]])
t1.rename_columns (Dictionary.from_vector [["name=(.*)".to_regex, "key:$1"]])
group_builder.specify "should work by index" <|
map = Map.from_vector [[0, "FirstColumn"], [-2, "Another"]]
map = Dictionary.from_vector [[0, "FirstColumn"], [-2, "Another"]]
expect_column_names ["FirstColumn", "beta", "Another", "delta"] <|
data.table.rename_columns map
@ -504,12 +504,12 @@ add_specs suite_builder setup =
data.table.rename_columns vec
group_builder.specify "should work by name" <|
map = Map.from_vector [["alpha", "FirstColumn"], ["delta", "Another"]]
map = Dictionary.from_vector [["alpha", "FirstColumn"], ["delta", "Another"]]
expect_column_names ["FirstColumn", "beta", "gamma", "Another"] <|
data.table.rename_columns map
group_builder.specify "should work by mixed Map" <|
map = Map.from_vector [["alpha", "FirstColumn"], [-1, "Another"]]
map = Dictionary.from_vector [["alpha", "FirstColumn"], [-1, "Another"]]
expect_column_names ["FirstColumn", "beta", "gamma", "Another"] <|
data.table.rename_columns map
@ -552,17 +552,17 @@ add_specs suite_builder setup =
fail_2.catch.message.should_contain "materialize"
group_builder.specify "should work by name case-insensitively" <|
map = Map.from_vector [["ALPHA", "FirstColumn"], ["DELTA", "Another"]]
map = Dictionary.from_vector [["ALPHA", "FirstColumn"], ["DELTA", "Another"]]
expect_column_names ["FirstColumn", "beta", "gamma", "Another"] <|
data.table.rename_columns map Case_Sensitivity.Insensitive
group_builder.specify "should work by name using regex" <|
map = Map.from_vector [["a.*".to_regex, "FirstColumn"]]
map = Dictionary.from_vector [["a.*".to_regex, "FirstColumn"]]
expect_column_names ["FirstColumn", "beta", "gamma", "delta"] <|
data.table.rename_columns map
group_builder.specify "should work by name using regex substitution" <|
map = Map.from_vector [["a(.*)".to_regex, "$1"]]
map = Dictionary.from_vector [["a(.*)".to_regex, "$1"]]
expect_column_names ["lpha", "beta", "gamma", "delta"] <|
data.table.rename_columns map
@ -591,7 +591,7 @@ add_specs suite_builder setup =
group_builder.specify "should correctly handle problems: unmatched names" <|
weird_name = '.*?-!@#!"'
map = Map.from_vector [["alpha", "FirstColumn"], ["omicron", "Another"], [weird_name, "Fixed"]]
map = Dictionary.from_vector [["alpha", "FirstColumn"], ["omicron", "Another"], [weird_name, "Fixed"]]
action = data.table.rename_columns map error_on_missing_columns=False on_problems=_
tester = expect_column_names ["FirstColumn", "beta", "gamma", "delta"]
err_checker err =
@ -603,7 +603,7 @@ add_specs suite_builder setup =
err.should_fail_with Missing_Input_Columns
group_builder.specify "should correctly handle problems: out of bounds indices" <|
map = Map.from_vector [[0, "FirstColumn"], [-1, "Another"], [100, "Boo"], [-200, "Nothing"], [300, "Here"]]
map = Dictionary.from_vector [[0, "FirstColumn"], [-1, "Another"], [100, "Boo"], [-200, "Nothing"], [300, "Here"]]
action = data.table.rename_columns map error_on_missing_columns=False on_problems=_
tester = expect_column_names ["FirstColumn", "beta", "gamma", "Another"]
err_checker err =
@ -615,12 +615,12 @@ add_specs suite_builder setup =
err.should_fail_with Missing_Input_Columns
group_builder.specify "should correctly handle edge-cases: aliased indices" <|
map1 = Map.from_vector [[1, "FirstColumn"], [-3, "FirstColumn"]]
map1 = Dictionary.from_vector [[1, "FirstColumn"], [-3, "FirstColumn"]]
t1 = data.table.rename_columns map1 on_problems=..Report_Error
Problems.assume_no_problems t1
expect_column_names ["alpha", "FirstColumn", "gamma", "delta"] t1
map2 = Map.from_vector [[1, "FirstColumn"], [-3, "DifferentName!"]]
map2 = Dictionary.from_vector [[1, "FirstColumn"], [-3, "DifferentName!"]]
t2 = data.table.rename_columns map2 on_problems=..Report_Error
t2.should_fail_with Ambiguous_Column_Rename
err = t2.catch . inner_error
@ -629,12 +629,12 @@ add_specs suite_builder setup =
group_builder.specify "should correctly handle edge-cases: aliased selectors" <|
t = table_builder [["alpha", [1,2,3]], ["bet", [4,5,6]]]
map1 = Map.from_vector [["a.*".to_regex, "AA"], [".*a".to_regex, "AA"]]
map1 = Dictionary.from_vector [["a.*".to_regex, "AA"], [".*a".to_regex, "AA"]]
t1 = t.rename_columns map1 on_problems=..Report_Error
Problems.assume_no_problems t1
expect_column_names ["AA", "bet"] t1
map2 = Map.from_vector [["a.*".to_regex, "StartsWithA"], [".*a".to_regex, "EndsWithA"]]
map2 = Dictionary.from_vector [["a.*".to_regex, "StartsWithA"], [".*a".to_regex, "EndsWithA"]]
t2 = t.rename_columns map2 on_problems=..Report_Error
t2.should_fail_with Ambiguous_Column_Rename
err = t2.catch . inner_error
@ -647,13 +647,13 @@ add_specs suite_builder setup =
This is to show that even if distinct rename patterns match the
same column, if the resulting rename is unambiguous, no error is
raised.
map3 = Map.from_vector [["a(.*)".to_regex, "$1A"], ["(.*)aa".to_regex, "$1aA"]]
map3 = Dictionary.from_vector [["a(.*)".to_regex, "$1A"], ["(.*)aa".to_regex, "$1aA"]]
t4 = t3.rename_columns map3 on_problems=..Report_Error
Problems.assume_no_problems t4
expect_column_names ["aaA", "bbb"] t4
group_builder.specify "should correctly handle problems: invalid names ''" <|
map = Map.from_vector [[1, ""]]
map = Dictionary.from_vector [[1, ""]]
[Problem_Behavior.Ignore, Problem_Behavior.Report_Warning, Problem_Behavior.Report_Error].each pb->
r = data.table.rename_columns map on_problems=pb
r.should_fail_with Invalid_Column_Names
@ -678,13 +678,13 @@ add_specs suite_builder setup =
Problems.test_problem_handling action problems tester
group_builder.specify "should correctly handle problems: new name is clashing with existing name of existing column" <|
map = Map.from_vector [["alpha", "beta"]]
map = Dictionary.from_vector [["alpha", "beta"]]
action = data.table.rename_columns map on_problems=_
tester = expect_column_names ["beta", "beta 1", "gamma", "delta"]
problems = [Duplicate_Output_Column_Names.Error ["beta"]]
Problems.test_problem_handling action problems tester
map2 = Map.from_vector [["beta", "alpha"]]
map2 = Dictionary.from_vector [["beta", "alpha"]]
action2 = data.table.rename_columns map2 on_problems=_
tester2 = expect_column_names ["alpha 1", "alpha", "gamma", "delta"]
problems2 = [Duplicate_Output_Column_Names.Error ["alpha"]]

View File

@ -211,7 +211,7 @@ postgres_specific_spec suite_builder create_connection_fn db_name setup =
column/table names and their lengths, this should not be a big
problem usually, so only a warning is issued. It may however lead
to data integrity issues in some very rare edge cases.
unsupported_encodings = Set.from_vector <|
unsupported_encodings = Hashset.from_vector <|
["EUC_JIS_2004", "LATIN6", "LATIN8", "MULE_INTERNAL", "SHIFT_JIS_2004"]
known_encodings.each encoding_name->
@ -706,14 +706,14 @@ add_postgres_specs suite_builder create_connection_fn db_name =
Common_Table_Operations.Main.add_specs suite_builder setup
## PRIVATE
supported_replace_params : Set Replace_Params
supported_replace_params : Hashset Replace_Params
supported_replace_params =
e0 = [Replace_Params.Value Text Case_Sensitivity.Default False, Replace_Params.Value Text Case_Sensitivity.Default True, Replace_Params.Value Text Case_Sensitivity.Sensitive False]
e1 = [Replace_Params.Value Text Case_Sensitivity.Sensitive True, Replace_Params.Value Text Case_Sensitivity.Insensitive False, Replace_Params.Value Text Case_Sensitivity.Insensitive True]
e2 = [Replace_Params.Value Regex Case_Sensitivity.Default False, Replace_Params.Value Regex Case_Sensitivity.Default True, Replace_Params.Value Regex Case_Sensitivity.Sensitive False]
e3 = [Replace_Params.Value Regex Case_Sensitivity.Sensitive True, Replace_Params.Value Regex Case_Sensitivity.Insensitive False, Replace_Params.Value Regex Case_Sensitivity.Insensitive True]
e4 = [Replace_Params.Value DB_Column Case_Sensitivity.Default False, Replace_Params.Value DB_Column Case_Sensitivity.Sensitive False]
Set.from_vector <| e0 + e1 + e2 + e3 + e4
Hashset.from_vector <| e0 + e1 + e2 + e3 + e4
add_table_specs suite_builder =
db_name = Environment.get "ENSO_POSTGRES_DATABASE"

View File

@ -356,10 +356,10 @@ sqlite_spec suite_builder prefix create_connection_func =
## PRIVATE
supported_replace_params : Set Replace_Params
supported_replace_params : Hashset Replace_Params
supported_replace_params =
e = [Replace_Params.Value Text Case_Sensitivity.Default False, Replace_Params.Value Text Case_Sensitivity.Sensitive False, Replace_Params.Value Text Case_Sensitivity.Default True, Replace_Params.Value Text Case_Sensitivity.Sensitive True, Replace_Params.Value Text Case_Sensitivity.Insensitive True]
Set.from_vector e
Hashset.from_vector e
## Reference to the database file that ensures the first test that uses it will
clean any leftover files from earlier runs.

View File

@ -123,8 +123,8 @@ add_specs suite_builder =
group_builder.specify "should be able to infer types for all supported operations" <|
dialect = Dialect.sqlite
internal_mapping = dialect.dialect_operations.operation_map
operation_type_mapping = SQLite_Type_Mapping.operations_map
internal_mapping = dialect.dialect_operations.operations_dict
operation_type_mapping = SQLite_Type_Mapping.operations_dict
operation_type_mapping.keys.sort . should_equal internal_mapping.keys.sort

View File

@ -562,7 +562,7 @@ add_specs suite_builder make_new_connection prefix persistent_connector=True =
e2.clashing_example_key_values.length . should_equal 1
x = e2.clashing_example_key_values.first
[1, 2, 3].should_contain x
counts = Map.from_vector [[1, 2], [2, 4], [3, 2]]
counts = Dictionary.from_vector [[1, 2], [2, 4], [3, 2]]
e2.clashing_example_row_count . should_equal (counts.at x)
# Will not find clashes if they are not in the first 1000 rows, in Output disabled mode.
@ -1201,14 +1201,14 @@ test_table_append group_builder (data : Data) source_table_builder target_table_
## If there are some additional tables, we add some timeout to allow
the database to do the cleaning up.
additional_tables = (Set.from_vector tables_immediately_after).difference (Set.from_vector existing_tables)
additional_tables = (Hashset.from_vector tables_immediately_after).difference (Hashset.from_vector existing_tables)
if additional_tables.is_empty then Nothing else
additional_table = additional_tables.to_vector.first
wait_until_temporary_table_is_deleted_after_closing_connection data.connection additional_table
# After the wait we check again and now there should be no additional tables.
tables_after_wait = data.connection.base_connection.get_tables_advanced types=Nothing include_hidden=True . at "Name" . to_vector
additional_tables_2 = (Set.from_vector tables_after_wait).difference (Set.from_vector existing_tables)
additional_tables_2 = (Hashset.from_vector tables_after_wait).difference (Hashset.from_vector existing_tables)
additional_tables_2.to_vector . should_equal []

View File

@ -116,7 +116,7 @@ add_specs suite_builder =
strategy.make_unique "abc" . should_equal "ab 10"
strategy.make_unique "abc" . should_equal "ab 11"
strategy.truncated_names . should_be_a Map
strategy.truncated_names . should_be_a Dictionary
strategy.truncated_names.get "abcdefgh" . should_equal "abcde"
# abc will contain the entry for the last truncated case
strategy.truncated_names.get "abc" . should_equal "ab 11"

View File

@ -227,30 +227,30 @@ add_specs suite_builder =
table.expand_column "cols" . should_equal expected
group_builder.specify "will work even if keys are not Text" <|
table = Table.new [["a", [1, 2]], ["b", [Map.from_vector [[1, "x"], [2, "y"]], Map.from_vector [[2, "z"], [3, "w"]]]]]
table = Table.new [["a", [1, 2]], ["b", [Dictionary.from_vector [[1, "x"], [2, "y"]], Dictionary.from_vector [[2, "z"], [3, "w"]]]]]
expected = Table.new [["a", [1, 2]], ["b 1", ["x", Nothing]], ["b 2", ["y", "z"]], ["b 3", [Nothing, "w"]]]
table.expand_column "b" . should_equal expected
table2 = Table.new [["a", [1, 2]], ["b", [Map.from_vector [[My_Mod_Type.Value 12, "x"], [My_Mod_Type.Value 23, "y"]], Map.from_vector [[My_Mod_Type.Value 32, "z"]]]]]
table2 = Table.new [["a", [1, 2]], ["b", [Dictionary.from_vector [[My_Mod_Type.Value 12, "x"], [My_Mod_Type.Value 23, "y"]], Dictionary.from_vector [[My_Mod_Type.Value 32, "z"]]]]]
expected2 = Table.new [["a", [1, 2]], ["b x%10=3", ["y", Nothing]], ["b x%10=2", ["x", "z"]]]
table2.expand_column "b" . should_equal expected2
group_builder.specify "will fail if text representation of keys is not unique" <|
k1 = My_Mod_Type.Value 12
k2 = My_Mod_Type.Value 32
m = Map.from_vector [[k1, "a"], [k2, "b"]]
m = Dictionary.from_vector [[k1, "a"], [k2, "b"]]
m.at k1 . should_equal "a"
m.at k2 . should_equal "b"
k1.to_text . should_equal "x%10=2"
k2.to_text . should_equal "x%10=2"
table = Table.new [["a", [1, 2]], ["b", [Map.from_vector [[k1, "x"], [k2, "y"]] , Map.from_vector []]]]
table = Table.new [["a", [1, 2]], ["b", [Dictionary.from_vector [[k1, "x"], [k2, "y"]] , Dictionary.from_vector []]]]
r = table.expand_column "b"
r.should_fail_with Illegal_Argument
r.catch.to_display_text . should_contain "keys are duplicated when converted to text"
group_builder.specify "will error when all objects have no fields" <|
table = Table.new [["aaa", [1, 2]], ["bbb", [Map.from_vector [], Map.from_vector []]], ["ccc", [5, 6]]]
table = Table.new [["aaa", [1, 2]], ["bbb", [Dictionary.from_vector [], Dictionary.from_vector []]], ["ccc", [5, 6]]]
r = table.expand_column "bbb"
r.should_fail_with Illegal_Argument
r.catch.message.should_contain "as all inputs had no fields"
@ -337,7 +337,7 @@ add_specs suite_builder =
table.expand_to_rows "bbb" . should_equal expected
group_builder.specify "Can expand Map" <|
values_to_expand = [Map.empty.insert "a" 10, Map.empty.insert "d" 40 . insert "b" 20, Map.empty.insert "c" 30]
values_to_expand = [Dictionary.singleton "a" 10, Dictionary.singleton "d" 40 . insert "b" 20, Dictionary.singleton "c" 30]
table = Table.new [["aaa", [1, 2, 3]], ["bbb", values_to_expand], ["ccc", [5, 6, 7]]]
expected = Table.new [["aaa", [1, 2, 2, 3]], ["bbb Key", ["a", "d", "b", "c"]], ["bbb", [10, 40, 20, 30]], ["ccc", [5, 6, 6, 7]]]
table.expand_to_rows "bbb" . should_equal expected

View File

@ -905,7 +905,7 @@ add_specs suite_builder =
if has_nulls then builder.append Nothing
if has_true then builder.append True
if has_false then builder.append False
in_vector_set = Set.from_vector in_vector
in_vector_set = Hashset.from_vector in_vector
vectors = [[True, False, Nothing], [Nothing, Nothing, Nothing], [False, False, True], [True, True, True], [False, False, False], [Nothing, True, True], [False, Nothing, False]]
vectors.each column_vector->