Allow Filter_Condition to be inverted. (#8861)

- Various linting fixes (doc comments and type annotations etc.).
- Add an action to determine if a `Filter_Condition` is keep or remove.

https://github.com/enso-org/enso/assets/4699705/69ba2bd3-8893-4237-acc4-eb01f534a209

- Remove `Not_In`, `Not_Contains` and `Not_Like` from `Filter_Condition`.

- Ability to use an `Expression` as a `Column_Ref`.

https://github.com/enso-org/enso/assets/4699705/16a2e030-f8f9-4f59-beca-2646f56fcb90
This commit is contained in:
James Dunkerley 2024-02-07 14:36:14 +00:00 committed by GitHub
parent 83fffd9c05
commit 0c39f8ec04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 531 additions and 467 deletions

View File

@ -612,6 +612,7 @@
- [Added `File_By_Line` type allowing processing a file line by line. New faster
JSON parser based off Jackson.][8719]
- [Implemented `Table.replace` for the in-memory backend.][8935]
- [Allow removing rows using a Filter_Condition.][8861]
[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
@ -880,6 +881,7 @@
[8849]: https://github.com/enso-org/enso/pull/8849
[8865]: https://github.com/enso-org/enso/pull/8865
[8935]: https://github.com/enso-org/enso/pull/8935
[8861]: https://github.com/enso-org/enso/pull/8861
#### Enso Compiler

View File

@ -1,9 +1,9 @@
from Standard.Base import all
from Standard.Base.Metadata import make_single_choice, Widget
from Standard.Base.Data.Enso_Cloud.Enso_Secret import as_hideable_value
from Standard.Base.Metadata import make_single_choice, Widget
polyglot java import org.enso.aws.ProfileReader
polyglot java import org.enso.aws.AwsCredential
polyglot java import org.enso.aws.ProfileReader
type AWS_Credential
## Access using IAM via an AWS profile.

View File

@ -146,7 +146,7 @@ read_text path (encoding=Encoding.utf_8) (on_problems=Problem_Behavior.Report_Wa
example_list_files =
Data.list_directory Examples.data_dir name_filter="**.md" recursive=True
list_directory : File -> Text | Nothing -> Boolean -> Vector File
list_directory directory:File name_filter:Text|Nothing=Nothing recursive:Boolean=False =
list_directory directory:File (name_filter:(Text | Nothing)=Nothing) recursive:Boolean=False =
directory . list name_filter=name_filter recursive=recursive
## ALIAS download, http get

View File

@ -1,5 +1,5 @@
import project.Data.Text.Text
import project.Data.Text.Encoding.Encoding
import project.Data.Text.Text
import project.Errors.Encoding_Error.Encoding_Error
from project.Data.Text.Extensions import all

View File

@ -255,7 +255,7 @@ type Enso_File
## UNSTABLE
Resolves a file or directory within this directory.
/ : Text -> Enso_File
/ : Text -> Enso_File ! Not_Found
/ self (name : Text) -> Enso_File ! Not_Found =
if self.is_directory.not then Error.throw (Illegal_Argument.Error "/ can only be used for directories") else
if name.contains "/" then Error.throw (Illegal_Argument.Error "Resolving sub-paths (/) is not implemented. Temporary workaround: use the `/` operator multiple times.") else

View File

@ -1,6 +1,6 @@
import project.Data.Base_64.Base_64
import project.Data.Enso_Cloud.Enso_File.Enso_File
import project.Data.Enso_Cloud.Enso_File.Enso_Asset_Type
import project.Data.Enso_Cloud.Enso_File.Enso_File
import project.Data.Enso_Cloud.Utils
import project.Data.Json.JS_Object
import project.Data.Text.Text
@ -15,8 +15,8 @@ import project.Network.HTTP.Request_Body.Request_Body
import project.Nothing.Nothing
import project.Runtime.Context
from project.Data.Boolean import Boolean, False, True
from project.Data.Text.Extensions import all
from project.Data.Enso_Cloud.Enso_File import list_assets
from project.Data.Text.Extensions import all
polyglot java import org.enso.base.enso_cloud.EnsoSecretHelper
polyglot java import org.enso.base.enso_cloud.HideableValue
@ -165,7 +165,7 @@ Derived_Secret_Value.from (that : Text) = Derived_Secret_Value.Plain_Text that
Derived_Secret_Value.from (that : Enso_Secret) = Derived_Secret_Value.Secret_Value that
## PRIVATE
as_hideable_value : Text | Enso_Secret | Nothing -> HideableValue
as_hideable_value : Text | Enso_Secret | Derived_Secret_Value -> HideableValue
as_hideable_value (value : Text | Enso_Secret | Derived_Secret_Value) = case value of
text : Text -> HideableValue.PlainValue.new text
secret : Enso_Secret -> HideableValue.SecretValue.new secret.id

View File

@ -1,6 +1,7 @@
private
import project.Data.Enso_Cloud.Enso_Secret.Enso_Secret
import project.Data.Enso_Cloud.Enso_Secret.Derived_Secret_Value
import project.Data.Enso_Cloud.Enso_Secret.Enso_Secret
import project.Data.Enso_Cloud.Errors.Not_Logged_In
import project.Data.Pair.Pair
import project.Data.Text.Text

View File

@ -21,25 +21,25 @@ polyglot java import org.enso.base.Regex_Utils
type Filter_Condition
## Is less than a value (or another column, in case of Table operations)?
Less than:Any
Less than:Any action:Filter_Action=Filter_Action.Keep
## Is less than or equal to a value (or another column, in case of Table operations)?
Equal_Or_Less than:Any
Equal_Or_Less than:Any action:Filter_Action=Filter_Action.Keep
## Is equal to a value (or another column, in case of Table operations)?
Equal to:Any
Equal to:Any action:Filter_Action=Filter_Action.Keep
## Is greater than or equal to a value (or another column, in case of Table operations)?
Equal_Or_Greater than:Any
Equal_Or_Greater than:Any action:Filter_Action=Filter_Action.Keep
## Is greater than a value (or another column, in case of Table operations)?
Greater than:Any
Greater than:Any action:Filter_Action=Filter_Action.Keep
## Is not equal to a value (or another column, in case of Table operations)?
Not_Equal to:Any
Not_Equal to:Any action:Filter_Action=Filter_Action.Keep
## Is between (inclusive) two values (or columns, in case of Table operations)?
Between lower:Any upper:Any
Between lower:Any upper:Any action:Filter_Action=Filter_Action.Keep
## Is equal to another value, ignoring case (Text only)?
@ -54,7 +54,7 @@ type Filter_Condition
This ensures that different ways of expressing the same character in
the underlying binary representation are considered equal.
@locale Locale.default_widget
Equal_Ignore_Case (to : Text | Any) (locale:Locale=Locale.default)
Equal_Ignore_Case (to : Text | Any) (locale:Locale=Locale.default) action:Filter_Action=Filter_Action.Keep
## Does the value start with a prefix (Text only)?
@ -62,7 +62,7 @@ type Filter_Condition
It accepts a Text value to check if the value contains it. In case of
Table operations, it can accept another column - then the corresponding
values from the source column and the provided column are checked.
Starts_With (prefix : Text | Any) (case_sensitivity:Case_Sensitivity=Case_Sensitivity.Default)
Starts_With (prefix : Text | Any) (case_sensitivity:Case_Sensitivity=Case_Sensitivity.Default) action:Filter_Action=Filter_Action.Keep
## Does the value end with a suffix (Text only)?
@ -70,7 +70,7 @@ type Filter_Condition
It accepts a Text value to check if the value contains it. In case of
Table operations, it can accept another column - then the corresponding
values from the source column and the provided column are checked.
Ends_With (suffix : Text | Any) (case_sensitivity:Case_Sensitivity=Case_Sensitivity.Default)
Ends_With (suffix : Text | Any) (case_sensitivity:Case_Sensitivity=Case_Sensitivity.Default) action:Filter_Action=Filter_Action.Keep
## Does the value contain the substring (Text only)?
@ -78,44 +78,36 @@ type Filter_Condition
It accepts a Text value to check if the value contains it. In case of
Table operations, it can accept another column - then the corresponding
values from the source column and the provided column are checked.
Contains (substring : Text | Any) (case_sensitivity:Case_Sensitivity=Case_Sensitivity.Default)
## Is the substring not present in the value (Text only)?
? Table Operations
It accepts a Text value to check if the value contains it. In case of
Table operations, it can accept another column - then the corresponding
values from the source column and the provided column are checked.
Not_Contains (substring : Text | Any) (case_sensitivity:Case_Sensitivity=Case_Sensitivity.Default)
Contains (substring : Text | Any) (case_sensitivity:Case_Sensitivity=Case_Sensitivity.Default) action:Filter_Action=Filter_Action.Keep
## Is equal to Nothing?
Is_Nothing
Is_Nothing action:Filter_Action=Filter_Action.Keep
## Is not equal to Nothing?
Not_Nothing
Not_Nothing action:Filter_Action=Filter_Action.Keep
## Is the value a NaN (Number only)?
Is_Nan
Is_Nan action:Filter_Action=Filter_Action.Keep
## Is the value infinite (Number only)?
Is_Infinite
Is_Infinite action:Filter_Action=Filter_Action.Keep
## Is the value finite (Number only)?
Finite numbers are ones that are not infinite nor NaN.
Is_Finite
Is_Finite action:Filter_Action=Filter_Action.Keep
## Is the value equal to True (Boolean only)?
Is_True
Is_True action:Filter_Action=Filter_Action.Keep
## Is the value equal to False (Boolean only)?
Is_False
Is_False action:Filter_Action=Filter_Action.Keep
## Is equal to "" or Nothing (Text only)?
Is_Empty
Is_Empty action:Filter_Action=Filter_Action.Keep
## Is not equal to "" and Nothing (Text only)?
Not_Empty
Not_Empty action:Filter_Action=Filter_Action.Keep
## Does the value match the SQL pattern (Text only)?
@ -134,26 +126,7 @@ type Filter_Condition
Due to this limitation, Unicode normalization has been disabled for
this function, so beware that some equivalent graphemes like 'ś' and
's\u0301' will not be matched.
Like (pattern : Text | Any)
## Does the value not match the SQL pattern (Text only)?
The pattern is interpreted according to the standard SQL convention:
- the `%` character matches any sequence of characters,
- the `_` character matches any single character,
- any other character is matched literally.
? Table Operations
It accepts a Text value to check if the value contains it. In case of
Table operations, it can accept another column - then the corresponding
values from the source column and the provided column are checked.
! Known Limitations.
The Truffle regex engine does not transparently handle normalization.
Due to this limitation, Unicode normalization has been disabled for
this function, so beware that some equivalent graphemes like 'ś' and
's\u0301' will not be matched.
Not_Like pattern:Text|Any
Like (pattern : Text | Any) action:Filter_Action=Filter_Action.Keep
## Is the value contained in `values`?
@ -167,21 +140,7 @@ type Filter_Condition
Using Columns can be particularly useful for Database operations, as
uploading a temporary table and using its column for an `Is_In` check
will likely be faster than using the vector directly.
Is_In values:Vector|Any
## Is the value not contained in `values`?
? Table Operations
It accepts a `Vector` of values. In case of Table operations, it can
also accept another column - then it acts as if `column.to_vector` was
passed - i.e. every element of the original table's column is checked
if it is contained in the provided column. The columns can have
different lengths.
Using Columns can be particularly useful for Database operations, as
uploading a temporary table and using its column for an `Not_In` check
will likely be faster than using the vector directly.
Not_In values:(Vector | Any)
Is_In values:Vector|Any action:Filter_Action=Filter_Action.Keep
## Converts a `Filter_Condition` condition into a predicate taking an
element and returning a value indicating whether the element should be
@ -190,57 +149,45 @@ type Filter_Condition
The predicate can handle `Nothing` values in all cases. However, the
predicate will raise an error if the value is not of the expected type.
to_predicate : (Any -> Boolean)
to_predicate self = case self of
to_predicate self =
base = case self of
# == does not need special handling for Nothing
Equal value -> ==value
Not_Equal value -> !=value
Less value -> handle_nothing (<value)
Equal_Or_Less value -> handle_nothing (<=value)
Equal_Or_Greater value -> handle_nothing (>=value)
Greater value -> handle_nothing (>value)
Between lower upper -> handle_nothing <| elem->
Equal value _ -> ==value
Not_Equal value _ -> !=value
Less value _ -> handle_nothing (<value)
Equal_Or_Less value _ -> handle_nothing (<=value)
Equal_Or_Greater value _ -> handle_nothing (>=value)
Greater value _ -> handle_nothing (>value)
Between lower upper _ -> handle_nothing <| elem->
(lower <= elem) && (elem <= upper)
Equal_Ignore_Case value locale ->
Equal_Ignore_Case value locale _ ->
handle_nothing <| txt-> (txt : Text).equals_ignore_case value locale
Starts_With prefix case_sensitivity ->
Starts_With prefix case_sensitivity _ ->
handle_nothing <| txt-> (txt : Text).starts_with prefix case_sensitivity
Ends_With suffix case_sensitivity ->
Ends_With suffix case_sensitivity _ ->
handle_nothing <| txt-> (txt : Text).ends_with suffix case_sensitivity
Contains substring case_sensitivity ->
Contains substring case_sensitivity _ ->
handle_nothing <| txt-> (txt : Text).contains substring case_sensitivity
Not_Contains substring case_sensitivity ->
handle_nothing <| txt-> (txt : Text).contains substring case_sensitivity . not
Is_Nothing -> elem -> case elem of
Is_Nothing _ -> elem -> case elem of
Nothing -> True
_ -> False
Not_Nothing -> elem -> case elem of
Not_Nothing _ -> elem -> case elem of
Nothing -> False
_ -> True
Is_Nan -> handle_nothing x-> (x:Number).is_nan
Is_Infinite -> handle_nothing x-> (x:Number).is_infinite
Is_Finite -> handle_nothing x-> (x:Number).is_finite
Is_True -> ==True
Is_False -> ==False
Is_Empty -> elem -> case elem of
Nothing -> True
"" -> True
_ -> False
Not_Empty -> elem -> case elem of
Nothing -> False
"" -> False
_ -> True
Like sql_pattern ->
Is_Nan _ -> handle_nothing x-> (x:Number).is_nan
Is_Infinite _ -> handle_nothing x-> (x:Number).is_infinite
Is_Finite _ -> handle_nothing x-> (x:Number).is_finite
Is_True _ -> handle_nothing b-> (b:Boolean)==True
Is_False _ -> handle_nothing b-> (b:Boolean)==False
Is_Empty _ -> elem-> elem.is_nothing || (elem : Text)==""
Not_Empty _ -> handle_nothing elem-> (elem : Text)!=""
Like sql_pattern _ ->
regex = sql_like_to_regex sql_pattern
handle_nothing <| regex.matches
Not_Like sql_pattern ->
regex = sql_like_to_regex sql_pattern
handle_nothing <| elem-> regex.matches elem . not
Is_In values ->
Is_In values _ ->
set = Set.from_vector values
set.contains
Not_In values ->
set = Set.from_vector values
elem-> set.contains elem . not
if self.action == Filter_Action.Keep then base else v -> (base v).not
## PRIVATE
Convert to a display representation of this Filter_Condition.
@ -250,34 +197,31 @@ type Filter_Condition
if case_sensitivity == Case_Sensitivity.Default then "" else " Case " + case_sensitivity.to_display_text
condition = case self of
Less value -> "<" + value.to_display_text
Equal_Or_Less value -> "<=" + value.to_display_text
Equal value -> "==" + value.to_display_text
Equal_Or_Greater value -> ">=" + value.to_display_text
Greater value -> ">" + value.to_display_text
Not_Equal value -> "!=" + value.to_display_text
Between lower upper -> "Between " + lower.to_display_text + " And " + upper.to_display_text
Equal_Ignore_Case value locale ->
Less value _ -> "<" + value.to_display_text
Equal_Or_Less value _ -> "<=" + value.to_display_text
Equal value _ -> "==" + value.to_display_text
Equal_Or_Greater value _ -> ">=" + value.to_display_text
Greater value _ -> ">" + value.to_display_text
Not_Equal value _ -> "!=" + value.to_display_text
Between lower upper _ -> "Between " + lower.to_display_text + " And " + upper.to_display_text
Equal_Ignore_Case value locale _ ->
suffix = if locale == Locale.default then "" else " (within locale " + locale.to_display_text + ")"
"Equal Ignore Case " + value.to_display_text + suffix
Starts_With prefix case_sensitivity -> "Starts With " + prefix.to_display_text + (render_case case_sensitivity)
Ends_With suffix case_sensitivity -> "Ends With " + suffix.to_display_text + (render_case case_sensitivity)
Contains substring case_sensitivity -> "Contains " + substring.to_display_text + (render_case case_sensitivity)
Not_Contains substring case_sensitivity -> "not Contains " + substring.to_display_text + (render_case case_sensitivity)
Is_Nothing -> "is Nothing"
Not_Nothing -> "is not Nothing"
Is_Nan -> "is NaN"
Is_Infinite -> "is Infinite"
Is_Finite -> "is Finite"
Is_True -> "is True"
Is_False -> "is False"
Is_Empty -> "is Empty"
Not_Empty -> "is not Empty"
Like sql_pattern -> "Like " + sql_pattern.to_display_text
Not_Like sql_pattern -> "not Like " + sql_pattern.to_display_text
Is_In values -> "is in " + values.to_display_text
Not_In values -> "is not in " + values.to_display_text
"Filter Condition: " + condition
Starts_With prefix case_sensitivity _ -> "Starts With " + prefix.to_display_text + (render_case case_sensitivity)
Ends_With suffix case_sensitivity _ -> "Ends With " + suffix.to_display_text + (render_case case_sensitivity)
Contains substring case_sensitivity _ -> "Contains " + substring.to_display_text + (render_case case_sensitivity)
Is_Nothing _ -> "is Nothing"
Not_Nothing _ -> "is not Nothing"
Is_Nan _ -> "is NaN"
Is_Infinite _ -> "is Infinite"
Is_Finite _ -> "is Finite"
Is_True _ -> "is True"
Is_False _ -> "is False"
Is_Empty _ -> "is Empty"
Not_Empty _ -> "is not Empty"
Like sql_pattern _ -> "Like " + sql_pattern.to_display_text
Is_In values _ -> "is in " + values.to_display_text
"Filter Condition: " + condition + (if self.action == Filter_Action.Keep then "" else " (Remove)")
## PRIVATE
Creates a Single_Choice Widget for delimiters.
@ -309,11 +253,8 @@ type Filter_Condition
options_builder.append "Starts_With"
options_builder.append "Ends_With"
options_builder.append "Contains"
options_builder.append "Not_Contains"
options_builder.append "Like"
options_builder.append "Not_Like"
options_builder.append "Is_In"
options_builder.append "Not_In"
options = options_builder.to_vector.map constructor_name->
name = constructor_name.replace "_" " "
@ -321,6 +262,13 @@ type Filter_Condition
[name, code]
make_single_choice options
## Specifies the action of a Filter_Condition.
type Filter_Action
## Items matching the filter are kept.
Keep
## Items matching the filter are removed.
Remove
## PRIVATE
sql_like_to_regex sql_pattern =

View File

@ -22,14 +22,19 @@ from project.Metadata.Widget import Single_Choice
polyglot java import com.fasterxml.jackson.core.JsonProcessingException
polyglot java import com.fasterxml.jackson.databind.JsonNode
polyglot java import com.fasterxml.jackson.databind.ObjectMapper
polyglot java import com.fasterxml.jackson.databind.node.ArrayNode
polyglot java import com.fasterxml.jackson.databind.node.JsonNodeType
polyglot java import com.fasterxml.jackson.databind.node.ObjectNode
polyglot java import com.fasterxml.jackson.databind.ObjectMapper
## PRIVATE
Jackson-based JSON Parser
type Java_Json
## ALIAS from text
GROUP Conversions
Parse a Text value into a `Jackson_Object` or an Enso primitive value
(like `Text`, `Number`, `Boolean`, `Nothing`), or a `Vector` of values.
parse : Text -> Nothing | Boolean | Number | Text | Vector | Jackson_Object
parse text:Text =
error_handler js_exception =
@ -60,6 +65,8 @@ read_json_array node =
## PRIVATE
type Jackson_Object
## PRIVATE
Creates a new `Jackson_Object` from an `ObjectNode`.
new : ObjectNode -> Jackson_Object
new object_node =
make_field_names object =

View File

@ -279,7 +279,7 @@ type JS_Object
obj.set_value "foo" "asdf"
# => {"foo":"asdf","baz":"quux"}
set_value : Text -> Any -> JS_Object
set_value self:JS_Object key:Text value = JS_Object.Value (set_value self.js_object key value.to_js_object)
set_value self key:Text value = JS_Object.Value (set_value self.js_object key value.to_js_object)
## PRIVATE
type JS_Object_Comparator

View File

@ -1,4 +1,5 @@
import project.Any.Any
import project.Data.Filter_Condition.Filter_Action
import project.Data.Filter_Condition.Filter_Condition
import project.Data.Text.Case.Case
import project.Data.Text.Text
@ -438,7 +439,7 @@ type Locale
predefined_locale_fields =
locale_meta = Meta.meta Locale
remove_us = locale_meta.methods + ["Value", "new", "default", "from_language_tag", "from_java", "predefined_locale_fields", "default_widget", "widget_options"]
Meta.Type.Value (Meta.type_of locale_meta.value) . methods . filter (Filter_Condition.Not_In remove_us) . sort
Meta.Type.Value (Meta.type_of locale_meta.value) . methods . filter (Filter_Condition.Is_In remove_us Filter_Action.Remove) . sort
## PRIVATE
widget_options : Vector Option

View File

@ -8,11 +8,10 @@ import project.Errors.Common.Incomparable_Values
import project.Errors.Common.Unsupported_Argument_Types
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Function.Function
import project.Internal.Rounding_Helpers
import project.Nothing.Nothing
import project.Panic.Panic
from project.Data.Boolean import Boolean, False, True
import project.Internal.Rounding_Helpers
from project.Internal.Number_Builtins import all
polyglot java import java.lang.Double

View File

@ -184,9 +184,8 @@ type Range
(0.up_to 7).filter (Filter_Condition.Greater than=3)
@filter range_default_filter_condition_widget
filter : (Filter_Condition | (Integer -> Boolean)) -> Vector Integer
filter self filter = case filter of
_ : Filter_Condition -> self.filter filter.to_predicate
predicate : Function ->
filter self filter =
predicate = unify_condition_or_predicate filter
builder = self.fold Vector.new_builder builder-> elem->
if predicate elem then builder.append elem else builder
builder.to_vector

View File

@ -308,7 +308,7 @@ type Date_Time
from Standard.Base import Date_Time
example_from_unix_epoch = Date_Time.from_unix_epoch_milliseconds 1601587200000
from_unix_epoch_milliseconds : Integer -> Time_Zone -> Date_Time
from_unix_epoch_milliseconds : Integer -> Date_Time
from_unix_epoch_milliseconds milliseconds:Integer =
unix_epoch_start + Duration.new milliseconds=milliseconds

View File

@ -5,8 +5,8 @@ import project.Meta
import project.Nothing.Nothing
import project.Panic.Panic
import project.System.File.File
import project.System.File_Format_Metadata.File_Format_Metadata
import project.System.File_Format.File_Format
import project.System.File_Format_Metadata.File_Format_Metadata
polyglot java import java.io.FileNotFoundException
polyglot java import java.io.IOException

View File

@ -5,6 +5,7 @@ import project.Data.Boolean
import project.Data.Enso_Cloud.Enso_File.Enso_File
import project.Data.Enso_Cloud.Enso_Secret.Enso_Secret
import project.Data.Enso_Cloud.Enso_User.Enso_User
import project.Data.Filter_Condition.Filter_Action
import project.Data.Filter_Condition.Filter_Condition
import project.Data.Index_Sub_Range.Index_Sub_Range
import project.Data.Interval.Bound
@ -102,6 +103,7 @@ export project.Data.Array.Array
export project.Data.Enso_Cloud.Enso_File.Enso_File
export project.Data.Enso_Cloud.Enso_Secret.Enso_Secret
export project.Data.Enso_Cloud.Enso_User.Enso_User
export project.Data.Filter_Condition.Filter_Action
export project.Data.Filter_Condition.Filter_Condition
export project.Data.Index_Sub_Range.Index_Sub_Range
export project.Data.Interval.Bound

View File

@ -1,13 +1,13 @@
import project.Data.Enso_Cloud.Enso_Secret.Enso_Secret
import project.Data.Enso_Cloud.Enso_Secret.Derived_Secret_Value
import project.Data.Enso_Cloud.Enso_Secret.Enso_Secret
import project.Data.Enso_Cloud.Utils as Cloud_Utils
import project.Data.Numbers.Integer
import project.Data.Text.Encoding.Encoding
import project.Data.Text.Text
import project.Nothing.Nothing
from project.Data.Boolean import Boolean, False, True
from project.Data.Ordering import all
from project.Data.Enso_Cloud.Enso_Secret import as_hideable_value
from project.Data.Ordering import all
from project.Data.Text.Extensions import all
polyglot java import org.graalvm.collections.Pair as Java_Pair
@ -22,7 +22,7 @@ type Header
- value: The header value.
Value name:Text value:(Text|Enso_Secret|Derived_Secret_Value)
## ALIAS Build a Header
## ALIAS build a header
Create a new Header.
@ -36,7 +36,7 @@ type Header
import Standard.Base.Network.HTTP.Header.Header
example_new = Header.new "My_Header" "my header's value"
new : Text -> Text | Enso_Secret -> Header
new : Text -> Text | Enso_Secret | Derived_Secret_Value -> Header
new name:Text value:(Text | Enso_Secret | Derived_Secret_Value) = Header.Value name value
## Create an "Accept" header.
@ -64,7 +64,7 @@ type Header
accept_all : Header
accept_all = Header.accept "*/*"
## ALIAS Build an Auth Header
## ALIAS build an auth header
Create an "Authorization" header.
@ -102,7 +102,7 @@ type Header
Arguments:
- token: The token.
authorization_bearer : Text -> Header
authorization_bearer : Text | Enso_Secret | Derived_Secret_Value -> Header
authorization_bearer (token : Text | Enso_Secret | Derived_Secret_Value) =
value = ((Derived_Secret_Value.from "Bearer ") + (Derived_Secret_Value.from token))
Header.authorization value.simplify

View File

@ -49,7 +49,7 @@ type Response_Body
- stream: The body of the response as an InputStream.
- metadata: File format metadata associated with the response.
- uri: The URI of the response.
new : InputStream -> Text -> URI -> Response_Body
new : InputStream -> File_Format_Metadata -> URI -> Response_Body
new stream (metadata : File_Format_Metadata) (uri : URI) =
input_stream = Input_Stream.new stream (HTTP_Error.handle_java_exceptions uri)
Response_Body.Raw_Stream input_stream metadata uri

View File

@ -155,7 +155,7 @@ type Random
import Standard.Base.Random.Random
d = Random.date (Date.new 2023 03 01) (Date.new 2023 10 15)
date : Date -> Date -> Boolean -> Date
date : Date -> Date -> Date
date min:Date max:Date = Random_Generator.global_random_generator.date min max
## GROUP Random
@ -297,7 +297,7 @@ type Random_Generator
Vector.from_polyglot_array array
## PRIVATE
date : Date -> Date -> Boolean -> Date
date : Date -> Date -> Date
date self start_date:Date end_date:Date =
date_range = start_date.up_to end_date include_end=True
date_range.at (self.integer 0 (date_range.length - 1))

View File

@ -14,11 +14,11 @@ from project.Data.Range.Extensions import all
from project.Data.Text.Extensions import all
from project.Logging import all
polyglot java import java.io.File as Java_File
polyglot java import java.nio.charset.Charset
polyglot java import org.enso.base.Array_Utils
polyglot java import org.enso.base.arrays.LongArrayList
polyglot java import org.enso.base.FileLineReader
polyglot java import java.io.File as Java_File
polyglot java import java.nio.charset.Charset
type File_By_Line
## Creates a new File_By_Line object.
@ -27,7 +27,7 @@ type File_By_Line
- file: The file to read.
- encoding: The encoding to use when reading the file (defaults to UTF 8).
- offset: The position within the file to read from (defaults to first byte).
new : File->Encoding->File_By_Line
new : File -> Encoding -> Integer -> File_By_Line
new file:File encoding:Encoding=Encoding.utf_8 offset:Integer=0 =
create_row_map =
row_map = LongArrayList.new

View File

@ -180,7 +180,7 @@ type Connection
True -> result
False ->
hidden_tables = self.hidden_table_registry.list_hidden_tables
result.filter "Name" (Filter_Condition.Not_In hidden_tables)
result.filter "Name" (Filter_Condition.Is_In hidden_tables Filter_Action.Remove)
## PRIVATE
Checks if the table with the given name exists in the database.

View File

@ -147,7 +147,7 @@ type Column
import Standard.Examples
example_at = Examples.integer_column.get 0 -1
get : Integer -> (Any | Nothing)
get : Integer -> Any -> Any | Nothing
get self (index : Integer) (~default=Nothing) =
self.read index+1 . get index default

View File

@ -32,9 +32,9 @@ from project.Internal.Column_Format import all
from project.Internal.Java_Exports import make_date_builder_adapter, make_string_builder
polyglot java import org.enso.base.Time_Utils
polyglot java import org.enso.table.data.mask.OrderMask
polyglot java import org.enso.table.data.column.operation.cast.CastProblemAggregator
polyglot java import org.enso.table.data.column.storage.Storage as Java_Storage
polyglot java import org.enso.table.data.mask.OrderMask
polyglot java import org.enso.table.data.table.Column as Java_Column
polyglot java import org.enso.table.error.ValueTypeMismatchException
polyglot java import org.enso.table.operations.OrderBuilder
@ -1063,7 +1063,11 @@ type Column
internal_is_empty : Column
internal_is_empty self =
new_name = naming_helper.concat [naming_helper.to_expression_text self, "is empty"]
run_vectorized_unary_op self Java_Storage.Maps.IS_EMPTY new_name fallback_fn=Filter_Condition.Is_Empty.to_predicate expected_result_type=Value_Type.Boolean skip_nulls=False
fallback x = case x of
_ : Text -> x == ""
Nothing -> True
_ -> False
run_vectorized_unary_op self Java_Storage.Maps.IS_EMPTY new_name fallback_fn=fallback expected_result_type=Value_Type.Boolean skip_nulls=False
## GROUP Standard.Base.Logical
Returns a column of booleans, with `True` items at the positions where
@ -2072,7 +2076,7 @@ type Column
import Standard.Examples
example_at = Examples.integer_column.get 0 -1
get : Integer -> (Any | Nothing)
get : Integer -> Any -> Any | Nothing
get self (index : Integer) (~default=Nothing) =
valid_index = (index >= 0) && (index < self.length)
if valid_index.not then default else

View File

@ -7,3 +7,6 @@ type Column_Ref
## Reference to a column by index in a table.
Index index:Integer
## Representation of an expression derived in a table.
Expression expression:Text

View File

@ -1,6 +1,6 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Data.Java_Json.Jackson_Object
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
## PRIVATE
A special type describing how to convert an object into a set of table

View File

@ -141,7 +141,7 @@ type Delimited_Format
## Creates a clone of this with a changed line ending style.
with_line_endings : Line_Ending_Style | Infer -> Delimited_Format
with_line_endings self (line_endings : Line_Ending_Style | Infer)=Infer =
with_line_endings self (line_endings : Line_Ending_Style | Infer = Infer) =
self.clone line_endings=line_endings
## Creates a clone of this with comment parsing enabled.

View File

@ -7,7 +7,7 @@ import project.Data.Column_Ref.Column_Ref
import project.Data.Expression.Expression_Error
import project.Data.Set_Mode.Set_Mode
import project.Data.Table.Table
from project.Errors import Existing_Column, Missing_Column, No_Such_Column
from project.Errors import Existing_Column, Invalid_Value_Type, Missing_Column, No_Such_Column
## PRIVATE
A helper type allowing to resolve column references in a context of an underlying table.
@ -23,12 +23,24 @@ type Table_Ref
at : Text | Integer -> Any ! No_Such_Column | Index_Out_Of_Bounds
at self selector=0 = self.underlying.at selector
## PRIVATE
Given an expression, create a derived column where each value is the
result of evaluating the expression for the row.
Arguments:
- expression: The expression to evaluate.
- on_problems: Specifies how to handle non-fatal problems, attaching a
warning by default.
evaluate_expression : Text -> Problem_Behavior -> Any ! No_Such_Column | Invalid_Value_Type | Expression_Error
evaluate_expression self expression on_problems=Report_Warning = self.underlying.evaluate_expression expression on_problems=on_problems
## PRIVATE
Resolve a Column_Ref to a Column, keeping any other values as-is.
resolve : Any -> Any ! No_Such_Column | Index_Out_Of_Bounds
resolve self value = case value of
Column_Ref.Name name -> self.at name
Column_Ref.Index index -> self.at index
Column_Ref.Expression expression -> self.evaluate_expression expression
_ -> value
## PRIVATE
@ -38,28 +50,26 @@ type Table_Ref
resolve_as_column self value = case value of
Column_Ref.Name name -> self.at name
Column_Ref.Index index -> self.at index
Column_Ref.Expression expression -> self.evaluate_expression expression
_ -> self.underlying.make_constant_column value
## PRIVATE
Transforms a condition, changing any Column_Ref instances into Column instances resolved in this table.
resolve_condition : Filter_Condition -> Filter_Condition
resolve_condition self condition = case condition of
Filter_Condition.Equal value -> Filter_Condition.Equal (self.resolve value)
Filter_Condition.Not_Equal value -> Filter_Condition.Not_Equal (self.resolve value)
Filter_Condition.Less value -> Filter_Condition.Less (self.resolve value)
Filter_Condition.Equal_Or_Less value -> Filter_Condition.Equal_Or_Less (self.resolve value)
Filter_Condition.Greater value -> Filter_Condition.Greater (self.resolve value)
Filter_Condition.Equal_Or_Greater value -> Filter_Condition.Equal_Or_Greater (self.resolve value)
Filter_Condition.Between lower upper -> Filter_Condition.Between (self.resolve lower) (self.resolve upper)
Filter_Condition.Equal_Ignore_Case value locale -> Filter_Condition.Equal_Ignore_Case (self.resolve value) locale
Filter_Condition.Starts_With prefix case_sensitivity -> Filter_Condition.Starts_With (self.resolve prefix) case_sensitivity
Filter_Condition.Ends_With prefix case_sensitivity -> Filter_Condition.Ends_With (self.resolve prefix) case_sensitivity
Filter_Condition.Contains prefix case_sensitivity -> Filter_Condition.Contains (self.resolve prefix) case_sensitivity
Filter_Condition.Not_Contains prefix case_sensitivity -> Filter_Condition.Not_Contains (self.resolve prefix) case_sensitivity
Filter_Condition.Like pattern -> Filter_Condition.Like (self.resolve pattern)
Filter_Condition.Not_Like pattern -> Filter_Condition.Not_Like (self.resolve pattern)
Filter_Condition.Is_In values -> Filter_Condition.Is_In (check_is_in_values "Is_In" values)
Filter_Condition.Not_In values -> Filter_Condition.Not_In (check_is_in_values "Not_In" values)
Filter_Condition.Equal value action -> Filter_Condition.Equal (self.resolve value) action
Filter_Condition.Not_Equal value action -> Filter_Condition.Not_Equal (self.resolve value) action
Filter_Condition.Less value action -> Filter_Condition.Less (self.resolve value) action
Filter_Condition.Equal_Or_Less value action -> Filter_Condition.Equal_Or_Less (self.resolve value) action
Filter_Condition.Greater value action -> Filter_Condition.Greater (self.resolve value) action
Filter_Condition.Equal_Or_Greater value action -> Filter_Condition.Equal_Or_Greater (self.resolve value) action
Filter_Condition.Between lower upper action -> Filter_Condition.Between (self.resolve lower) (self.resolve upper) action
Filter_Condition.Equal_Ignore_Case value locale action -> Filter_Condition.Equal_Ignore_Case (self.resolve value) locale action
Filter_Condition.Starts_With prefix case_sensitivity action -> Filter_Condition.Starts_With (self.resolve prefix) case_sensitivity action
Filter_Condition.Ends_With prefix case_sensitivity action -> Filter_Condition.Ends_With (self.resolve prefix) case_sensitivity action
Filter_Condition.Contains prefix case_sensitivity action -> Filter_Condition.Contains (self.resolve prefix) case_sensitivity action
Filter_Condition.Like pattern action -> Filter_Condition.Like (self.resolve pattern) action
Filter_Condition.Is_In values action -> Filter_Condition.Is_In (check_is_in_values "Is_In" values) action
_ -> condition
## PRIVATE

View File

@ -13,67 +13,60 @@ from project.Errors import Nothing_Value_In_Filter_Condition
It also performs validation and will throw errors if unexpected column types
are encountered.
make_filter_column source_column filter_condition on_problems = case filter_condition of
make_filter_column source_column filter_condition on_problems =
base_column = case filter_condition of
# Equality
Equal value ->
Equal value _ ->
warn_on_nothing_in_comparison filter_condition value <|
Warning.with_suspended source_column source_column->
Warning.with_suspended value value->
on_problems.escalate_warnings <|
source_column == value
Not_Equal value ->
Not_Equal value _ ->
warn_on_nothing_in_comparison filter_condition value <|
Warning.with_suspended source_column source_column->
Warning.with_suspended value value->
on_problems.escalate_warnings <|
source_column != value
# Nothing
Is_Nothing -> source_column.is_nothing
Not_Nothing -> source_column.is_nothing.not
Is_Nothing _ -> source_column.is_nothing
Not_Nothing _ -> source_column.is_nothing.not
# Boolean
Is_True ->
Value_Type.expect_boolean source_column <| source_column
Is_False -> source_column.not
Is_True _ -> Value_Type.expect_boolean source_column <| source_column
Is_False _ -> Value_Type.expect_boolean source_column <| source_column.not
# Comparisons
Less value ->
Less value _ ->
warn_on_nothing_in_comparison filter_condition value <|
source_column < value
Equal_Or_Less value ->
Equal_Or_Less value _ ->
warn_on_nothing_in_comparison filter_condition value <|
source_column <= value
Equal_Or_Greater value ->
Equal_Or_Greater value _ ->
warn_on_nothing_in_comparison filter_condition value <|
source_column >= value
Greater value ->
Greater value _ ->
warn_on_nothing_in_comparison filter_condition value <|
source_column > value
Between lower upper ->
Between lower upper _ ->
warn_on_nothing_in_comparison filter_condition lower <|
warn_on_nothing_in_comparison filter_condition upper <|
source_column.between lower upper
# Text
Equal_Ignore_Case value locale ->
Equal_Ignore_Case value locale _ ->
source_column.equals_ignore_case value locale
Starts_With prefix case_sensitivity ->
Starts_With prefix case_sensitivity _ ->
source_column.starts_with prefix case_sensitivity
Ends_With suffix case_sensitivity ->
Ends_With suffix case_sensitivity _ ->
source_column.ends_with suffix case_sensitivity
Contains substring case_sensitivity ->
Contains substring case_sensitivity _ ->
source_column.contains substring case_sensitivity
Not_Contains substring case_sensitivity ->
source_column.contains substring case_sensitivity . not
Is_Empty ->
source_column.is_empty
Not_Empty ->
source_column.is_empty.not
Like pattern ->
source_column.like pattern
Not_Like pattern ->
source_column.like pattern . not
Is_Empty _ -> source_column.is_empty
Not_Empty _ -> source_column.is_empty.not
Like pattern _ -> source_column.like pattern
# Numeric
Is_Nan -> source_column.is_nan
Is_Infinite -> source_column.is_infinite
Is_Finite ->
Is_Nan _ -> source_column.is_nan
Is_Infinite _ -> source_column.is_infinite
Is_Finite _ ->
is_infinite_column = source_column.is_infinite
is_nan_column = source_column.is_nan
## We check is_nan_column for error, since some Database backends may
@ -81,12 +74,12 @@ make_filter_column source_column filter_condition on_problems = case filter_cond
if is_nan_column.is_error then is_infinite_column.not else
(is_infinite_column || is_nan_column).not
# Vector
Is_In values ->
Is_In values _ ->
warn_on_nothing_in_comparison_vector filter_condition values <|
source_column.is_in values
Not_In values ->
warn_on_nothing_in_comparison_vector filter_condition values <|
source_column.is_in values . not
if filter_condition.action == Filter_Action.Keep then base_column else
## We need to fill in the Nothing values and then negate the result.
(base_column.fill_nothing False).not
## Attach a warning if the provided value is `Nothing`.
warn_on_nothing_in_comparison : Filter_Condition -> Any -> Any -> Any

View File

@ -85,7 +85,8 @@ make_column_name_vector_selector table display=Display.Always =
make_column_ref_by_name_selector : Table -> Display -> Widget
make_column_ref_by_name_selector table display=Display.Always =
col_names_options = table.column_names.map (name -> Option name "(Column_Ref.Name "+name.pretty+")")
Single_Choice values=col_names_options display=display
expression_option = Option "<Expression>" "(Column_Ref.Expression '["+table.column_names.first+"]')"
Single_Choice values=(col_names_options+[expression_option]) display=display
## PRIVATE
Same as `make_column_ref_by_name_selector` but also highlights an option for
@ -94,7 +95,8 @@ make_column_ref_or_text_value_selector : Table -> Display -> Widget
make_column_ref_or_text_value_selector table display=Display.Always =
col_names_options = table.column_names.map (name -> Option name "(Column_Ref.Name "+name.pretty+")")
custom_text_option = Option "'custom text'" "''"
Single_Choice values=(col_names_options+[custom_text_option]) display=display
expression_option = Option "<Expression>" "(Column_Ref.Expression '["+table.column_names.first+"]')"
Single_Choice values=(col_names_options+[custom_text_option, expression_option]) display=display
## PRIVATE
If `column_source` is Nothing, `Column_Ref` options will not be added.
@ -104,7 +106,9 @@ make_fill_default_value_selector column_source=Nothing include_custom_text=False
column_source.column_names.map (name -> Option name "(Column_Ref.Name "+name.pretty+")")
custom_text_option = if include_custom_text then [Option "'custom text'" "''"] else []
previous_value_option = [Option 'Previous Value' 'Previous_Value']
Single_Choice values=(previous_value_option+col_names_options+custom_text_option) display=display
default_column = if column_source.is_nothing then '""' else column_source.name.pretty
expression_option = Option "<Expression>" '(Column_Ref.Expression \''+default_column+'\')'
Single_Choice values=(previous_value_option+col_names_options+custom_text_option+expression_option) display=display
## PRIVATE
Make a filter condition selector.
@ -124,7 +128,6 @@ make_filter_condition_selector table display=Display.Always =
builder.append (Option "Starts With" fqn+".Starts_With" [["prefix", col_names]])
builder.append (Option "Ends With" fqn+".Ends_With" [["suffix", col_names]])
builder.append (Option "Contains" fqn+".Contains" [["substring", col_names]])
builder.append (Option "Not Contains" fqn+".Not_Contains" [["substring", col_names]])
builder.append (Option "Is Nothing" fqn+".Is_Nothing")
builder.append (Option "Is Not Nothing" fqn+".Not_Nothing")
builder.append (Option "Is Finite" fqn+".Is_Finite")
@ -135,9 +138,7 @@ make_filter_condition_selector table display=Display.Always =
builder.append (Option "Is Empty" fqn+".Is_Empty")
builder.append (Option "Is Not Empty" fqn+".Not_Empty")
builder.append (Option "Like" fqn+".Like" [["pattern", col_names]])
builder.append (Option "Not Like" fqn+".Not_Like" [["pattern", col_names]])
builder.append (Option "Is In" fqn+".Is_In")
builder.append (Option "Not In" fqn+".Not_In")
Single_Choice builder.to_vector display=display
## PRIVATE

View File

@ -5,13 +5,13 @@ from Standard.Base.Runtime import State
import project.Clue.Clue
import project.Group.Group
import project.Spec_Result.Spec_Result
import project.Spec.Spec
import project.Spec_Result.Spec_Result
import project.Suite.Suite
import project.Suite_Config.Suite_Config
import project.Test_Result.Test_Result
import project.Test_Reporter
import project.Test.Test
import project.Test_Reporter
import project.Test_Result.Test_Result
run_group_with_filter : Group -> (Regex|Text|Nothing) -> Vector Test_Result
run_group_with_filter (group : Group) (spec_filter : (Regex|Text|Nothing)) =
@ -28,7 +28,7 @@ run_group (group : Group) =
run_specs_from_group group.specs group
run_specs_from_group : Vector Spec -> Text -> Vector Test_Result
run_specs_from_group : Vector Spec -> Group -> Vector Test_Result
run_specs_from_group (specs : Vector Spec) (group : Group) =
case specs.is_empty of
True -> []

View File

@ -1,9 +1,10 @@
import project.Problems
import project.Suite.Suite
import project.Test.Test
import project.Problems
from project.Extensions import all
export project.Problems
export project.Suite.Suite
export project.Test.Test
export project.Problems
from project.Extensions export all

View File

@ -28,7 +28,7 @@ type Suite_Builder
`specify` method on group builder. See its docs.
- pending: Contains a reason for why the test group should be ignored. If Nothing, the test
is not ignored.
group : Text -> (Group_Builder -> Any) -> Nothing
group : Text -> (Group_Builder -> Any) -> (Text | Nothing) -> Nothing
group self (name:Text) (fn : (Group_Builder -> Any)) (pending : (Text | Nothing) = Nothing) =
group_builder = Group_Builder.Impl
case pending of
@ -62,7 +62,7 @@ type Suite
- group_filter: Filter for group names.
- spec_filter: Filter for spec names.
- should_exit: If true, executes `System.exit` at the end.
run_with_filter : (Regex | Text | Nothing) -> (Regex | Text | Nothing) -> Nothing
run_with_filter : (Regex | Text | Nothing) -> (Regex | Text | Nothing) -> Boolean -> Nothing
run_with_filter self group_filter=Nothing spec_filter=Nothing should_exit=True =
config = Suite_Config.from_environment
@ -99,9 +99,11 @@ type Suite
False ->
failed_tests == 0
## Gets the names of all the groups in this suite.
group_names self =
self.groups.map (_.name)
## Print the structure of the suite to the console.
print_all self =
IO.println "Test Suite:"
self.groups.each group->

View File

@ -1,14 +1,14 @@
from Standard.Base import all
from Standard.Base.Errors.Common import Uninitialized_State
from Standard.Base.Runtime import State
import project.Test_Result.Test_Result
import project.Clue.Clue
import project.Group.Group
import project.Suite.Suite
import project.Suite.Suite_Builder
import project.Spec.Spec
import project.Spec_Result.Spec_Result
import project.Suite.Suite
import project.Suite.Suite_Builder
import project.Test_Result.Test_Result
## Contains only static methods
type Test
@ -65,6 +65,7 @@ type Test
Test.expect_panic_with Examples.My_Error <|
IO.println 'hello'
Examples.throw_panic
IO.println 'this is not reached'
expect_panic : Any -> Any -> Test_Result
expect_panic matcher ~action = Test.expect_panic_with action matcher

View File

@ -1,11 +1,11 @@
private
from Standard.Base import all
from Standard.Base.Runtime import assert
import Standard.Base.Runtime.Context
from Standard.Base.Runtime import assert
import project.Suite_Config.Suite_Config
import project.Spec_Result.Spec_Result
import project.Suite_Config.Suite_Config
import project.Test.Test
import project.Test_Result.Test_Result

View File

@ -5,17 +5,23 @@ import project.Spec_Result.Spec_Result
## A wrapper for `Spec_Result` that contains also name of the group and name of the spec.
type Test_Result
## PRIVATE
Impl (group_name : Text) (spec_name : Text) (spec_result : Spec_Result) (time_taken : Duration)
## PRIVATE
Render as Test_Result as Text.
to_text self =
"'" + self.group_name + "' '" + self.spec_name + "': " + self.spec_result.to_text
## Was the test pending?
is_pending self =
self.spec_result.is_pending
## Was the test successful?
is_success self =
self.spec_result.is_success
## Was the test a failure?
is_fail self =
self.spec_result.is_fail

View File

@ -124,7 +124,7 @@ public final class BoolStorage extends Storage<Boolean> {
*/
private Storage<?> fillMissingBoolean(boolean arg) {
final var newValues = (BitSet) values.clone();
if (arg) {
if (arg != negated) {
newValues.or(isMissing);
} else {
newValues.andNot(isMissing);

View File

@ -77,9 +77,9 @@ add_specs suite_builder =
Json.parse '{"start": 15, "end": 20, "step": 3}' . into Range . should_equal (Range.Between 15 20 3)
group_builder.specify "should be able to deserialize using into for multiple constructors" <|
Json.parse '{"than": 2}' . into Filter_Condition . should_fail_with Illegal_Argument
Json.parse '{"constructor": "Less", "than": 2}' . into Filter_Condition . should_equal (Filter_Condition.Less 2)
Json.parse '{"constructor": "NotARealOne", "than": 2}' . into Filter_Condition . should_fail_with Illegal_Argument
Json.parse '{"than": 2}' . into Statistic . should_fail_with Illegal_Argument
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]])

View File

@ -36,6 +36,7 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
l.any (Filter_Condition.Greater 1) . should_be_true
l.any (Filter_Condition.Less 0) . should_be_false
l.any (Filter_Condition.Equal_Or_Greater 1 Filter_Action.Remove) . should_be_false
Test.expect_panic_with (l.any "invalid arg") Type_Error
@ -47,6 +48,7 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
l.all (Filter_Condition.Greater 0) . should_be_true
l.all (Filter_Condition.Less 3) . should_be_false
l.any (Filter_Condition.Less 0 Filter_Action.Remove) . should_be_true
Test.expect_panic_with (l.all "invalid arg") Type_Error
@ -70,6 +72,7 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
empty.find (==1) if_missing=Nothing . should_equal Nothing
l.find (Filter_Condition.Greater 1) . should_equal 2
l.find (Filter_Condition.Less 2 Filter_Action.Remove) . should_equal 2
Test.expect_panic_with (l.find "invalid arg") Type_Error
@ -90,6 +93,7 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
l.index_of 2 start=-4 . should_fail_with Index_Out_Of_Bounds
l.index_of (Filter_Condition.Greater 1) . should_equal 1
l.index_of (Filter_Condition.Less 3 Filter_Action.Remove) . should_equal 2
l.index_of "invalid arg" . should_equal Nothing
group_builder.specify "should allow finding the last index of an element in the list with `.last_index_of`" <|
@ -110,6 +114,7 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
ll.last_index_of (Filter_Condition.Greater 1) . should_equal 5
ll.last_index_of (Filter_Condition.Less 3) . should_equal 4
ll.last_index_of (Filter_Condition.Greater 2 Filter_Action.Remove) . should_equal 4
ll.last_index_of "invalid arg" . should_equal Nothing
group_builder.specify "should allow checking if the list is empty with `.is_empty`" <|
@ -129,6 +134,7 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
group_builder.specify "should filter elements by Filter_Condition" <|
list = [1, 2, 3, 4, 5].to_list
list.filter (Filter_Condition.Greater than=3) . should_equal [4, 5].to_list
list.filter (Filter_Condition.Greater than=3 action=Filter_Action.Remove) . should_equal [1, 2, 3].to_list
list.filter (Filter_Condition.Less than=3.5) . should_equal [1, 2, 3].to_list
list.filter (Filter_Condition.Equal to=3) . should_equal (List.Cons 3 List.Nil)
list.filter (Filter_Condition.Not_Equal to=3) . should_equal [1, 2, 4, 5].to_list
@ -136,14 +142,23 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
list.filter (Filter_Condition.Equal_Or_Less than=(-1)) . should_equal List.Nil
list.filter (Filter_Condition.Between 2 4) . should_equal [2, 3, 4].to_list
list.filter (Filter_Condition.Is_In [7, 3, 2]) . should_equal [2, 3].to_list
list.filter (Filter_Condition.Not_In [7, 3, 2]) . should_equal [1, 4, 5].to_list
Test.expect_panic Type_Error (list.filter (Filter_Condition.Starts_With "a"))
list.filter Filter_Condition.Is_True . should_equal List.Nil
list.filter Filter_Condition.Is_False . should_equal List.Nil
list.filter Filter_Condition.Is_Nothing . should_equal List.Nil
list.filter Filter_Condition.Not_Nothing . should_equal list
## Boolean based filters should fail with type error
Test.expect_panic Type_Error (list.filter Filter_Condition.Is_True)
Test.expect_panic Type_Error (list.filter Filter_Condition.Is_False)
## Text based filters should fail with type error
Test.expect_panic Type_Error (list.filter (Filter_Condition.Starts_With "a"))
Test.expect_panic Type_Error (list.filter (Filter_Condition.Ends_With "a"))
Test.expect_panic Type_Error (list.filter (Filter_Condition.Contains "a"))
Test.expect_panic Type_Error (list.filter (Filter_Condition.Equal_Ignore_Case "a"))
Test.expect_panic Type_Error (list.filter (Filter_Condition.Like "a%"))
Test.expect_panic Type_Error (list.filter Filter_Condition.Is_Empty)
Test.expect_panic Type_Error (list.filter Filter_Condition.Not_Empty)
txt = ["aaa", "bbb", "abab", "cccc", "baaa", "ś"].to_list
txt.filter (Filter_Condition.Contains "a") . should_equal ["aaa", "abab", "baaa"].to_list
txt.filter (Filter_Condition.Contains 'A' Case_Sensitivity.Sensitive) . should_equal [].to_list
@ -151,12 +166,6 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
txt.filter (Filter_Condition.Contains 's\u0301') . should_equal ["ś"].to_list
txt.filter (Filter_Condition.Contains 'S\u0301' Case_Sensitivity.Sensitive) . should_equal [].to_list
txt.filter (Filter_Condition.Contains 'S\u0301' Case_Sensitivity.Insensitive) . should_equal ["ś"].to_list
txt.filter (Filter_Condition.Not_Contains "a") . should_equal ["bbb", "cccc", "ś"].to_list
txt.filter (Filter_Condition.Not_Contains "A" Case_Sensitivity.Sensitive) . should_equal ["aaa", "bbb", "abab", "cccc", "baaa", "ś"].to_list
txt.filter (Filter_Condition.Not_Contains "A" Case_Sensitivity.Insensitive) . should_equal ["bbb", "cccc", "ś"].to_list
txt.filter (Filter_Condition.Not_Contains 's\u0301') . should_equal ["aaa", "bbb", "abab", "cccc", "baaa"].to_list
txt.filter (Filter_Condition.Not_Contains 'S\u0301' Case_Sensitivity.Sensitive) . should_equal ["aaa", "bbb", "abab", "cccc", "baaa", "ś"].to_list
txt.filter (Filter_Condition.Not_Contains 'S\u0301' Case_Sensitivity.Insensitive) . should_equal ["aaa", "bbb", "abab", "cccc", "baaa"].to_list
txt.filter (Filter_Condition.Starts_With "a") . should_equal ["aaa", "abab"].to_list
txt.filter (Filter_Condition.Starts_With "A" Case_Sensitivity.Sensitive) . should_equal [].to_list
txt.filter (Filter_Condition.Starts_With "A" Case_Sensitivity.Insensitive) . should_equal ["aaa", "abab"].to_list
@ -173,7 +182,6 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
["abab", "aaabaaaa", "ba"].to_list.filter (Filter_Condition.Like "ba") . should_equal (List.Cons "ba" List.Nil)
["abab", "aaabaaaa"].to_list.filter (Filter_Condition.Like "_ba_") . should_equal ["abab"].to_list
["abab", "aaabaaaa"].to_list.filter (Filter_Condition.Like "%ba__%") . should_equal ["aaabaaaa"].to_list
["abab", "aaabaaaa"].to_list.filter (Filter_Condition.Not_Like "%ba%") . should_equal List.Nil
mixed = [1, Nothing, "b"].to_list
mixed.filter Filter_Condition.Is_Nothing . should_equal (List.Cons Nothing List.Nil)
@ -192,6 +200,10 @@ add_specs suite_builder = suite_builder.group "List" group_builder->
r2.first . should_equal (List.Cons 2 (List.Cons 3 List.Nil))
r2.second . should_equal (List.Cons 1 List.Nil)
r2r = l.partition (Filter_Condition.Greater than=1 action=Filter_Action.Remove)
r2r.second . should_equal (List.Cons 2 (List.Cons 3 List.Nil))
r2r.first . should_equal (List.Cons 1 List.Nil)
r3 = l.partition (Filter_Condition.Equal_Or_Greater than=10)
r3.first . should_equal List.Nil
r3.second . should_equal l

View File

@ -152,30 +152,48 @@ add_specs suite_builder = suite_builder.group "Range" group_builder->
group_builder.specify "should filter elements by Filter_Condition" <|
range = 1.up_to 6
range.filter (Filter_Condition.Greater than=3) . should_equal [4, 5]
range.filter (Filter_Condition.Less than=3.5) . should_equal [1, 2, 3]
range.filter (Filter_Condition.Equal to=3) . should_equal [3]
range.filter (Filter_Condition.Not_Equal to=3) . should_equal [1, 2, 4, 5]
range.filter (Filter_Condition.Equal_Or_Greater than=3) . should_equal [3, 4, 5]
range.filter (Filter_Condition.Equal_Or_Less than=(-1)) . should_equal []
range.filter (Filter_Condition.Greater 3) . should_equal [4, 5]
range.filter (Filter_Condition.Greater 3 Filter_Action.Remove) . should_equal [1, 2, 3]
range.filter (Filter_Condition.Less 3.5) . should_equal [1, 2, 3]
range.filter (Filter_Condition.Less 3.5 Filter_Action.Remove) . should_equal [4, 5]
range.filter (Filter_Condition.Equal 3) . should_equal [3]
range.filter (Filter_Condition.Equal 3 Filter_Action.Remove) . should_equal [1, 2, 4, 5]
range.filter (Filter_Condition.Not_Equal 3) . should_equal [1, 2, 4, 5]
range.filter (Filter_Condition.Not_Equal 3 Filter_Action.Remove) . should_equal [3]
range.filter (Filter_Condition.Equal_Or_Greater 3) . should_equal [3, 4, 5]
range.filter (Filter_Condition.Equal_Or_Greater 3 Filter_Action.Remove) . should_equal [1, 2]
range.filter (Filter_Condition.Equal_Or_Less (-1)) . should_equal []
range.filter (Filter_Condition.Equal_Or_Less (-1) Filter_Action.Remove) . should_equal [1, 2, 3, 4, 5]
range.filter (Filter_Condition.Between 2 4) . should_equal [2, 3, 4]
range.filter (Filter_Condition.Between 2.1 4.5) . should_equal [3, 4]
range.filter (Filter_Condition.Between 2 4 Filter_Action.Remove) . should_equal [1, 5]
range.filter (Filter_Condition.Is_In [7, 3, 2]) . should_equal [2, 3]
range.filter (Filter_Condition.Not_In [7, 3, 2]) . should_equal [1, 4, 5]
range.filter (Filter_Condition.Is_In [7, 3, 2] Filter_Action.Remove) . should_equal [1, 4, 5]
Test.expect_panic Type_Error (range.filter (Filter_Condition.Starts_With "a"))
Test.expect_panic Type_Error (range.filter (Filter_Condition.Equal_Ignore_Case "a"))
range.filter (Filter_Condition.Like "a%") . should_fail_with Type_Error
range.filter (Filter_Condition.Not_Like "a_") . should_fail_with Type_Error
range.filter Filter_Condition.Is_Nan . should_equal []
range.filter (Filter_Condition.Is_Nan Filter_Action.Remove) . should_equal [1, 2, 3, 4, 5]
range.filter Filter_Condition.Is_Infinite . should_equal []
range.filter (Filter_Condition.Is_Infinite Filter_Action.Remove) . should_equal [1, 2, 3, 4, 5]
range.filter Filter_Condition.Is_Finite . should_equal [1, 2, 3, 4, 5]
range.filter Filter_Condition.Is_True . should_equal []
range.filter Filter_Condition.Is_False . should_equal []
range.filter (Filter_Condition.Is_Finite Filter_Action.Remove) . should_equal []
range.filter Filter_Condition.Is_Nothing . should_equal []
range.filter (Filter_Condition.Is_Nothing Filter_Action.Remove) . should_equal [1, 2, 3, 4, 5]
range.filter Filter_Condition.Not_Nothing . should_equal [1, 2, 3, 4, 5]
range.filter Filter_Condition.Is_Empty . should_equal []
range.filter Filter_Condition.Not_Empty . should_equal [1, 2, 3, 4, 5]
range.filter (Filter_Condition.Not_Nothing Filter_Action.Remove) . should_equal []
## Text based filters should fail with type error
Test.expect_panic Type_Error (range.filter (Filter_Condition.Starts_With "a"))
Test.expect_panic Type_Error (range.filter (Filter_Condition.Ends_With "a"))
Test.expect_panic Type_Error (range.filter (Filter_Condition.Contains "a"))
Test.expect_panic Type_Error (range.filter (Filter_Condition.Equal_Ignore_Case "a"))
Test.expect_panic Type_Error (range.filter (Filter_Condition.Like "a%"))
Test.expect_panic Type_Error (range.filter Filter_Condition.Is_Empty)
Test.expect_panic Type_Error (range.filter Filter_Condition.Not_Empty)
## Boolean based filters should fail with type error
Test.expect_panic Type_Error (range.filter Filter_Condition.Is_True)
Test.expect_panic Type_Error (range.filter Filter_Condition.Is_False)
group_builder.specify "should allow to partition its elements" <|
elements = 0.up_to 10
@ -183,14 +201,19 @@ add_specs suite_builder = suite_builder.group "Range" group_builder->
r1.first . should_equal [0, 2, 4, 6, 8]
r1.second . should_equal [1, 3, 5, 7, 9]
r2 = elements.partition (Filter_Condition.Greater than=3)
r2 = elements.partition (Filter_Condition.Greater 3)
r2.first . should_equal [4, 5, 6, 7, 8, 9]
r2.second . should_equal [0, 1, 2, 3]
r2r = elements.partition (Filter_Condition.Greater 3 Filter_Action.Remove)
r2r.first . should_equal [0, 1, 2, 3]
r2r.second . should_equal [4, 5, 6, 7, 8, 9]
group_builder.specify "should allow iteration" <|
vec_mut = Vector.new_builder
1.up_to 6 . each (i -> vec_mut.append i)
vec_mut.to_vector . should_equal [1, 2, 3, 4, 5]
group_builder.specify "should allow iteration, with error propagation and early exit" <|
vec_mut = Vector.new_builder
result = 1.up_to 6 . each_propagate i->
@ -198,6 +221,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder->
vec_mut.append i
result . should_fail_with Illegal_Argument
vec_mut.to_vector . should_equal [1, 2]
group_builder.specify "should allow iteration with index" <|
vec_mut = Vector.new_builder
5.up_to 8 . each_with_index ix-> elem->
@ -208,36 +232,46 @@ add_specs suite_builder = suite_builder.group "Range" group_builder->
5.up_to 10 . with_step 2 . each_with_index ix-> elem->
vec_mut_2.append (Pair.new ix elem)
vec_mut_2.to_vector . should_equal [Pair.new 0 5, Pair.new 1 7, Pair.new 2 9]
group_builder.specify "should be able to be folded" <|
1.up_to 6 . fold 0 (+) . should_equal 15
1.up_to 1 . fold 123 (+) . should_equal 123
group_builder.specify "should be able to perform a running fold" <|
1.up_to 6 . running_fold 0 (+) . should_equal [1, 3, 6, 10, 15]
1.up_to 1 . running_fold 123 (+) . should_equal []
group_builder.specify "should be able to be reduced" <|
1.up_to 6 . reduce (+) . should_equal 15
1.up_to 6 . with_step 2 . reduce (+) . should_equal 9
1.up_to 1 . reduce (+) . should_fail_with Empty_Error
1.up_to 1 . reduce (+) 0 . should_equal 0
group_builder.specify "should check all" <|
1.up_to 10 . all (> 0) . should_be_true
1.up_to 10 . all (< 0) . should_be_false
1.up_to 10 . all (Filter_Condition.Greater 10) . should_be_false
1.up_to 10 . all (Filter_Condition.Greater 10 Filter_Action.Remove) . should_be_true
Test.expect_panic_with (1.up_to 10 . all "invalid arg") Type_Error
group_builder.specify "should check any" <|
1.up_to 10 . any (> 5) . should_be_true
1.up_to 10 . any (> 10) . should_be_false
1.up_to 10 . any (Filter_Condition.Greater 5) . should_be_true
1.up_to 10 . any (Filter_Condition.Equal_Or_Greater 1 Filter_Action.Remove) . should_be_false
Test.expect_panic_with (1.up_to 10 . any "invalid arg") Type_Error
group_builder.specify "should find elements" <|
1.up_to 10 . find (> 5) . should_equal 6
1.up_to 10 . find (Filter_Condition.Greater than=5) . should_equal 6
1.up_to 10 . find (Filter_Condition.Greater 5) . should_equal 6
1.up_to 10 . find (Filter_Condition.Equal_Or_Less 3 Filter_Action.Remove) . should_equal 4
1.up_to 10 . find (> 10) . should_be_a Nothing
1.up_to 10 . find (v-> v%4 == 0) start=6 . should_equal 8
1.up_to 10 . find (< 5) start=6 . should_be_a Nothing
1.up_to 10 . find (< 5) start=10 . should_fail_with Index_Out_Of_Bounds
1.up_to 10 . find (< 5) start=10 . catch . should_equal (Index_Out_Of_Bounds.Error 10 10)
Test.expect_panic_with (1.up_to 10 . find "invalid arg") Type_Error
group_builder.specify "should find index of elements" <|
1.up_to 10 . index_of (> 5) . should_equal 5
1.up_to 10 . index_of 7 . should_equal 6
@ -253,9 +287,11 @@ add_specs suite_builder = suite_builder.group "Range" group_builder->
1.up_to 10 . index_of (< 5) start=10 . catch . should_equal (Index_Out_Of_Bounds.Error 10 10)
1.up_to 10 . index_of (< 5) start=-1 . should_equal Nothing
1.up_to 10 . index_of (< 5) start=-9 . should_equal 0
1.up_to 10 . index_of (Filter_Condition.Greater than=5) . should_equal 5
1.up_to 10 . index_of (Filter_Condition.Greater 5) . should_equal 5
1.up_to 10 . index_of (Filter_Condition.Less 5 Filter_Action.Remove) . should_equal 4
1.up_to 10 . index_of "invalid arg" . should_fail_with Illegal_Argument
1.up_to 10 . index_of 2.5 . should_fail_with Illegal_Argument
group_builder.specify "should find last index of elements" <|
1.up_to 10 . last_index_of (> 5) . should_equal 8
1.up_to 10 . last_index_of 7 . should_equal 6
@ -271,11 +307,14 @@ add_specs suite_builder = suite_builder.group "Range" group_builder->
1.up_to 10 . last_index_of (< 5) start=9 . should_fail_with Index_Out_Of_Bounds
1.up_to 10 . last_index_of (< 5) start=10 . catch . should_equal (Index_Out_Of_Bounds.Error 10 9)
1.up_to 10 . last_index_of (< 5) start=-10 . should_fail_with Index_Out_Of_Bounds
1.up_to 10 . last_index_of (Filter_Condition.Greater than=5) . should_equal 8
1.up_to 10 . last_index_of (Filter_Condition.Greater 5) . should_equal 8
1.up_to 10 . last_index_of (Filter_Condition.Greater 5 Filter_Action.Remove) . should_equal 4
1.up_to 10 . last_index_of "invalid arg" . should_fail_with Illegal_Argument
1.up_to 10 . last_index_of 2.5 . should_fail_with Illegal_Argument
group_builder.specify "should allow conversion to vector" <|
1.up_to 6 . to_vector . should_equal [1, 2, 3, 4, 5]
group_builder.specify "should allow reversing" <|
1.up_to 6 . reverse . should_equal (5.down_to 0)
5.down_to 0 . reverse . should_equal (1.up_to 6)
@ -297,7 +336,6 @@ add_specs suite_builder = suite_builder.group "Range" group_builder->
5.down_to 0 . contains 2.5 . should_fail_with Illegal_Argument
5.down_to 0 . contains 3.0 . should_fail_with Illegal_Argument
verify_contains range expected unexpected =
expected.each x->
if range.contains x . not then
@ -305,10 +343,12 @@ add_specs suite_builder = suite_builder.group "Range" group_builder->
unexpected.each x->
if range.contains x then
Test.fail "Range "+range.to_text+" unexpectedly contained "+x.to_text+"."
build_with_each range =
builder = Vector.new_builder
range.each builder.append
builder.to_vector
group_builder.specify "should behave correctly if it is empty" <|
check_empty_range r =
r.is_empty . should_be_true

View File

@ -164,6 +164,7 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
vec.any (ix -> ix < 0) . should_be_false
vec.any (Filter_Condition.Greater 0) . should_be_true
vec.any (Filter_Condition.Greater 0 Filter_Action.Remove) . should_be_false
vec.any (Filter_Condition.Less 3) . should_be_true
vec.any (Filter_Condition.Less 0) . should_be_false
@ -175,6 +176,7 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
vec.all (ix -> ix < 5) . should_be_false
vec.all (Filter_Condition.Greater 0) . should_be_true
vec.all (Filter_Condition.Greater 0 Filter_Action.Remove) . should_be_false
vec.all (Filter_Condition.Less 3) . should_be_false
Test.expect_panic_with matcher=Type_Error (vec.all "invalid argument")
@ -203,55 +205,84 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
vec.filter (x -> x < 0) . should_equal []
vec.filter (x -> if x == 2 then Error.throw <| My_Error.Error "foo" else True) . should_fail_with My_Error
group_builder.specify "should filter elements by Filter_Condition" <|
group_builder.specify "should filter numerical elements by Filter_Condition" <|
vec = alter [1, 2, 3, 4, 5]
vec.filter (Filter_Condition.Greater than=3) . should_equal [4, 5]
vec.filter (Filter_Condition.Less than=3.5) . should_equal [1, 2, 3]
vec.filter (Filter_Condition.Equal to=3) . should_equal [3]
vec.filter (Filter_Condition.Equal to=3.0) . should_equal [3]
vec.filter (Filter_Condition.Equal to=3.1) . should_equal []
vec.filter (Filter_Condition.Not_Equal to=3) . should_equal [1, 2, 4, 5]
vec.filter (Filter_Condition.Equal_Or_Greater than=3) . should_equal [3, 4, 5]
vec.filter (Filter_Condition.Equal_Or_Less than=(-1)) . should_equal []
vec.filter (Filter_Condition.Greater 3) . should_equal [4, 5]
vec.filter (Filter_Condition.Greater 3 Filter_Action.Remove) . should_equal [1, 2, 3]
vec.filter (Filter_Condition.Less 3.5) . should_equal [1, 2, 3]
vec.filter (Filter_Condition.Less 3.5 Filter_Action.Remove) . should_equal [4, 5]
vec.filter (Filter_Condition.Equal 3) . should_equal [3]
vec.filter (Filter_Condition.Equal 3 Filter_Action.Remove) . should_equal [1, 2, 4, 5]
vec.filter (Filter_Condition.Not_Equal 3) . should_equal [1, 2, 4, 5]
vec.filter (Filter_Condition.Not_Equal 3 Filter_Action.Remove) . should_equal [3]
vec.filter (Filter_Condition.Equal_Or_Greater 3) . should_equal [3, 4, 5]
vec.filter (Filter_Condition.Equal_Or_Greater 3 Filter_Action.Remove) . should_equal [1, 2]
vec.filter (Filter_Condition.Equal_Or_Less (-1)) . should_equal []
vec.filter (Filter_Condition.Equal_Or_Less (-1) Filter_Action.Remove) . should_equal [1, 2, 3, 4, 5]
vec.filter (Filter_Condition.Between 2 4) . should_equal [2, 3, 4]
vec.filter (Filter_Condition.Between 2.1 4.5) . should_equal [3, 4]
vec.filter (Filter_Condition.Between 2 4 Filter_Action.Remove) . should_equal [1, 5]
vec.filter (Filter_Condition.Is_In [7, 3, 2, 2, 2]) . should_equal [2, 3]
vec.filter (Filter_Condition.Is_In []) . should_equal []
vec.filter (Filter_Condition.Not_In [7, 3, 2, 2]) . should_equal [1, 4, 5]
vec.filter (Filter_Condition.Is_In [7, 3, 2] Filter_Action.Remove) . should_equal [1, 4, 5]
Test.expect_panic_with (vec.filter (Filter_Condition.Starts_With "a")) Type_Error
vec.filter Filter_Condition.Is_True . should_equal []
vec.filter Filter_Condition.Is_False . should_equal []
vec.filter Filter_Condition.Is_Nothing . should_equal []
vec.filter Filter_Condition.Not_Nothing . should_equal vec
vec.filter (Filter_Condition.Is_Nothing Filter_Action.Remove) . should_equal [1, 2, 3, 4, 5]
vec.filter Filter_Condition.Not_Nothing . should_equal [1, 2, 3, 4, 5]
vec.filter (Filter_Condition.Not_Nothing Filter_Action.Remove) . should_equal []
txtvec = ["aaa", "bbb", "abab", "cccc", "baaa", "ś"]
numvec = alter [1, 2.5, Number.nan, Number.positive_infinity, Number.negative_infinity, 0]
# We need to use to_text because NaN!=NaN
numvec.filter Filter_Condition.Is_Nan . map .to_text . should_equal ["NaN"]
numvec.filter (Filter_Condition.Is_Nan Filter_Action.Remove) . should_equal [1, 2.5, Number.positive_infinity, Number.negative_infinity, 0]
numvec.filter Filter_Condition.Is_Infinite . should_equal [Number.positive_infinity, Number.negative_infinity]
numvec.filter (Filter_Condition.Is_Infinite Filter_Action.Remove) . length . should_equal 4
numvec.filter Filter_Condition.Is_Finite . should_equal [1, 2.5, 0]
numvec.filter (Filter_Condition.Is_Finite Filter_Action.Remove) . length . should_equal 3
## Text based filters should fail with type error
Test.expect_panic Type_Error (vec.filter (Filter_Condition.Starts_With "a"))
Test.expect_panic Type_Error (vec.filter (Filter_Condition.Ends_With "a"))
Test.expect_panic Type_Error (vec.filter (Filter_Condition.Contains "a"))
Test.expect_panic Type_Error (vec.filter (Filter_Condition.Equal_Ignore_Case "a"))
Test.expect_panic Type_Error (vec.filter (Filter_Condition.Like "a%"))
Test.expect_panic Type_Error (vec.filter Filter_Condition.Is_Empty)
Test.expect_panic Type_Error (vec.filter Filter_Condition.Not_Empty)
## Boolean based filters should fail with type error
Test.expect_panic Type_Error (vec.filter Filter_Condition.Is_True)
Test.expect_panic Type_Error (vec.filter Filter_Condition.Is_False)
group_builder.specify "should filter text elements by Filter_Condition" <|
txtvec = alter ["aaa", "bbb", "abab", "cccc", "baaa", "ś"]
txtvec.filter (Filter_Condition.Contains "a") . should_equal ["aaa", "abab", "baaa"]
txtvec.filter (Filter_Condition.Contains "a" action=Filter_Action.Remove) . should_equal ["bbb", "cccc", "ś"]
txtvec.filter (Filter_Condition.Contains 'A' Case_Sensitivity.Sensitive) . should_equal []
txtvec.filter (Filter_Condition.Contains 'A' Case_Sensitivity.Insensitive) . should_equal ["aaa", "abab", "baaa"]
txtvec.filter (Filter_Condition.Contains 's\u0301') . should_equal ["ś"]
txtvec.filter (Filter_Condition.Contains 'S\u0301' Case_Sensitivity.Sensitive) . should_equal []
txtvec.filter (Filter_Condition.Contains 'S\u0301' Case_Sensitivity.Insensitive) . should_equal ["ś"]
txtvec.filter (Filter_Condition.Not_Contains "a") . should_equal ["bbb", "cccc", "ś"]
txtvec.filter (Filter_Condition.Not_Contains "A" Case_Sensitivity.Sensitive) . should_equal ["aaa", "bbb", "abab", "cccc", "baaa", "ś"]
txtvec.filter (Filter_Condition.Not_Contains "A" Case_Sensitivity.Insensitive) . should_equal ["bbb", "cccc", "ś"]
txtvec.filter (Filter_Condition.Not_Contains 's\u0301') . should_equal ["aaa", "bbb", "abab", "cccc", "baaa"]
txtvec.filter (Filter_Condition.Not_Contains 'S\u0301' Case_Sensitivity.Sensitive) . should_equal ["aaa", "bbb", "abab", "cccc", "baaa", "ś"]
txtvec.filter (Filter_Condition.Not_Contains 'S\u0301' Case_Sensitivity.Insensitive) . should_equal ["aaa", "bbb", "abab", "cccc", "baaa"]
Test.expect_panic_with (txtvec.filter (Filter_Condition.Contains 42)) Unsupported_Argument_Types
txtvec.filter (Filter_Condition.Starts_With "a") . should_equal ["aaa", "abab"]
txtvec.filter (Filter_Condition.Starts_With "a" action=Filter_Action.Remove) . should_equal ["bbb", "cccc", "baaa", "ś"]
txtvec.filter (Filter_Condition.Starts_With "A" Case_Sensitivity.Sensitive) . should_equal []
txtvec.filter (Filter_Condition.Starts_With "A" Case_Sensitivity.Insensitive) . should_equal ["aaa", "abab"]
Test.expect_panic_with (txtvec.filter (Filter_Condition.Starts_With 42)) Unsupported_Argument_Types
txtvec.filter (Filter_Condition.Ends_With "a") . should_equal ["aaa", "baaa"]
txtvec.filter (Filter_Condition.Ends_With "a" action=Filter_Action.Remove) . should_equal ["bbb", "abab", "cccc", "ś"]
txtvec.filter (Filter_Condition.Ends_With "A" Case_Sensitivity.Sensitive) . should_equal []
txtvec.filter (Filter_Condition.Ends_With "A" Case_Sensitivity.Insensitive) . should_equal ["aaa", "baaa"]
Test.expect_panic_with (txtvec.filter (Filter_Condition.Ends_With 42)) Unsupported_Argument_Types
txtvec.filter (Filter_Condition.Less than="a") . should_equal []
txtvec.filter (Filter_Condition.Greater than="b") . should_equal ["bbb", "cccc", "baaa", "ś"]
txtvec.filter (Filter_Condition.Between "b" "c") . should_equal ["bbb", "baaa"]
Test.expect_panic_with (txtvec.filter (Filter_Condition.Starts_With 42)) Unsupported_Argument_Types
txtvec.filter Filter_Condition.Is_True . should_equal []
txtvec.filter (Filter_Condition.Is_In [1, 2]) . should_equal []
txtvec.filter (Filter_Condition.Is_In ["bbb", 's\u0301', "bbb", "FOOBAR"]) . should_equal ["bbb", "ś"]
## Boolean based filters should fail with type error
Test.expect_panic Type_Error (txtvec.filter Filter_Condition.Is_True)
Test.expect_panic Type_Error (txtvec.filter Filter_Condition.Is_False)
alter ["", Nothing, " ", "a"] . filter (Filter_Condition.Is_Empty) . should_equal ["", Nothing]
alter ["", Nothing, " ", "a"] . filter (Filter_Condition.Not_Empty) . should_equal [" ", "a"]
alter ["abab", "aaabaaaa", "ba"] . filter (Filter_Condition.Like "ba") . should_equal ["ba"]
@ -259,18 +290,23 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
alter ["abab", "aaabaaaa"] . filter (Filter_Condition.Like "%ba__%") . should_equal ["aaabaaaa"]
alter ["aaaa", "bbbbb", "[ab]aaaa"] . filter (Filter_Condition.Like "[ab]%") . should_equal ["[ab]aaaa"]
alter ["f.txt", "abc.*"] . filter (Filter_Condition.Like "%.*") . should_equal ["abc.*"]
alter ["f.txt", "abc.*"] . filter (Filter_Condition.Not_Like "%.*") . should_equal ["f.txt"]
txt2 = alter ['a\n\n\n', 'a\n', 'a\n\n\nb', 'a\nb', 'caa\nbb']
txt2.filter (Filter_Condition.Like 'a_') . should_equal ['a\n']
txt2.filter (Filter_Condition.Like 'a_' Filter_Action.Remove) . should_equal ['a\n\n\n', 'a\n\n\nb', 'a\nb', 'caa\nbb']
txt2.filter (Filter_Condition.Like 'a%') . should_equal ['a\n\n\n', 'a\n', 'a\n\n\nb', 'a\nb']
txt2.filter (Filter_Condition.Like 'a_b') . should_equal ['a\nb']
txt2.filter (Filter_Condition.Like '%\nb') . should_equal ['a\n\n\nb', 'a\nb']
## NUmber based filters should fail with type error
Test.expect_panic Type_Error (txtvec.filter Filter_Condition.Is_Nan)
Test.expect_panic Type_Error (txtvec.filter Filter_Condition.Is_Infinite)
Test.expect_panic Type_Error (txtvec.filter Filter_Condition.Is_Finite)
group_builder.specify "should filter unicode text elements by Filter_Condition" <|
txt3 = alter ['śnieg', 's\u0301nieg', 'X', 'połać', 'połac\u0301']
txt3.filter (Filter_Condition.Starts_With 'ś') . should_equal ['śnieg', 's\u0301nieg']
txt3.filter (Filter_Condition.Contains 'ś') . should_equal ['śnieg', 's\u0301nieg']
txt3.filter (Filter_Condition.Not_Contains 'ś') . should_equal ['X', 'połać', 'połac\u0301']
txt3.filter (Filter_Condition.Ends_With 'ś') . should_equal []
txt3.filter (Filter_Condition.Ends_With 'ć') . should_equal ['połać', 'połac\u0301']
## There is a bug with Java Regex in Unicode normalized mode (CANON_EQ) with quoting.
@ -284,19 +320,12 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
txt4.filter (Filter_Condition.Equal_Ignore_Case 's\u0301') . should_equal ['ś', 'Ś']
txt4.filter (Filter_Condition.Equal_Ignore_Case 'FFI') . should_equal ['ffi']
group_builder.specify "should filter mixed elements by Filter_Condition" <|
mixed = alter [1, Nothing, "b"]
mixed.filter Filter_Condition.Is_Nothing . should_equal [Nothing]
mixed.filter Filter_Condition.Not_Nothing . should_equal [1, "b"]
mixed.filter Filter_Condition.Is_Empty . should_equal [Nothing]
mixed.filter Filter_Condition.Not_Empty . should_equal [1, "b"]
numvec = alter [1, 2.5, Number.nan, Number.positive_infinity, Number.negative_infinity, 0]
# We need to use to_text because NaN!=NaN
numvec.filter Filter_Condition.Is_Nan . map .to_text . should_equal ["NaN"]
numvec.filter Filter_Condition.Is_Infinite . should_equal [Number.positive_infinity, Number.negative_infinity]
numvec.filter Filter_Condition.Is_Finite . should_equal [1, 2.5, 0]
Test.expect_panic Type_Error (txtvec.filter Filter_Condition.Is_Finite)
Test.expect_panic Type_Error (mixed.filter Filter_Condition.Is_Empty)
Test.expect_panic Type_Error (mixed.filter Filter_Condition.Not_Empty)
(alter [2, "a"]).filter (Filter_Condition.Greater 1) . should_fail_with Incomparable_Values
@ -320,8 +349,6 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
txtvec.filter (Filter_Condition.Starts_With "a") . should_equal ["abab"]
txtvec.filter (Filter_Condition.Ends_With "a") . should_equal ["baaa"]
txtvec.filter (Filter_Condition.Like "b%a") . should_equal ["baaa"]
# Nothing is not included in the negation either
txtvec.filter (Filter_Condition.Not_Like "b%a") . should_equal ["abab", "cccc", "BAAA"]
(alter ["a", 2, Nothing, 3]).filter (Filter_Condition.Is_In [Nothing, 2]) . should_equal [2, Nothing]
@ -340,11 +367,12 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
(alter [1, 2, 3, 4] . filter_with_index ix-> _-> if ix == 1 then Error.throw <| My_Error.Error "foo" else True) . should_fail_with My_Error
group_builder.specify "should partition elements" <|
vec = alter [1, 2, 3, 4, 5]
vec.partition (x -> x % 2 == 0) . should_equal <| Pair.new [2, 4] [1, 3, 5]
vec = alter [1, 2, 3, 4, 5, Nothing]
vec.partition (x -> x.is_nothing.not && x%2==0) . should_equal <| Pair.new [2, 4] [1, 3, 5, Nothing]
(vec . partition x-> if x == 1 then Error.throw <| My_Error.Error "foo" else True) . should_fail_with My_Error
vec.partition (Filter_Condition.Between 2 4) . should_equal <| Pair.new [2, 3, 4] [1, 5]
vec.partition (Filter_Condition.Between 2 4) . should_equal <| Pair.new [2, 3, 4] [1, 5, Nothing]
vec.partition (Filter_Condition.Between 2 4 Filter_Action.Remove) . should_equal <| Pair.new [1, 5, Nothing] [2, 3, 4]
Test.expect_panic_with matcher=Type_Error (vec.partition "invalid arg")
@ -462,6 +490,7 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
input.find (x -> x%3 == 3) if_missing=Nothing . should_equal Nothing
input.find (Filter_Condition.Greater 5) . should_equal 6
input.find (Filter_Condition.Less 5 Filter_Action.Remove) . should_equal 5
Test.expect_panic_with matcher=Type_Error (input.find "invalid arg")
alter ["b", "A", "c"] . find (Filter_Condition.Equal_Ignore_Case "a") . should_equal "A"
@ -480,6 +509,7 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
input.index_of 2 start=-11 . should_fail_with Index_Out_Of_Bounds
input.index_of (Filter_Condition.Equal_Or_Greater 2) . should_equal 1
input.index_of (Filter_Condition.Less 3 Filter_Action.Remove) . should_equal 2
input.index_of "text" . should_equal Nothing
group_builder.specify "should allow finding the last index of a value" <|
@ -494,6 +524,7 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
input.last_index_of 2 start=-11 . should_fail_with Index_Out_Of_Bounds
input.last_index_of (Filter_Condition.Equal_Or_Greater 2) . should_equal input.length-2
input.last_index_of (Filter_Condition.Less 3 Filter_Action.Remove) . should_equal 6
input.last_index_of "text" . should_equal Nothing
group_builder.specify "should be convertible to a list" <|

View File

@ -596,6 +596,18 @@ add_specs suite_builder setup =
actual.at "col1" . to_vector . should_equal [1000, 200, 1000, 400, 500, 1000]
actual.column_names . should_equal ["col0", "col_between", "col1", "def"]
group_builder.specify "fill_nothing should cope with notted boolean columns" <|
t = table_builder [["col0", [True, False, Nothing, True]], ["colTrue", [True, True, True, True]], ["colFalse", [False, False, False, False]]]
s = t.set (t.at "col0" . not) "col1"
s_false = s.fill_nothing ["col1"] False
s_false.at "col1" . to_vector . should_equal [False, True, False, False]
s_false_2 = s.fill_nothing ["col1"] (s.at "colFalse")
s_false_2.at "col1" . to_vector . should_equal [False, True, False, False]
s_true = s.fill_nothing ["col1"] True
s_true.at "col1" . to_vector . should_equal [False, True, True, False]
s_true_2 = s.fill_nothing ["col1"] (s.at "colTrue")
s_true_2.at "col1" . to_vector . should_equal [False, True, True, False]
suite_builder.group prefix+"Table.text_replace" group_builder->
data = Data.setup create_connection_fn

View File

@ -136,6 +136,7 @@ add_specs suite_builder setup =
group_builder.specify "if" <|
t = table_builder [["A", [1, 100]], ["B", [10, 40]], ["C", [23, 55]]]
t.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Greater than=(Column_Ref.Name "B"))) "Z" . at "Z" . to_vector . should_equal [False, True]
t.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Greater than=(Column_Ref.Name "B") Filter_Action.Remove)) "Z" . at "Z" . to_vector . should_equal [True, False]
t.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Greater than=20) "T" "F") "Z" . at "Z" . to_vector . should_equal ["F", "T"]
t.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Less than=20) (Column_Ref.Name "B") (Column_Ref.Name "C")) "Z" . at "Z" . to_vector . should_equal [10, 55]
@ -148,19 +149,15 @@ add_specs suite_builder setup =
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Equal_Ignore_Case "C") "==" "!=") "Z" . at "Z" . to_vector . should_equal ["!=", "=="]
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Is_In ["x", "a", "dd"]) "TT" "FF") "Z" . at "Z" . to_vector . should_equal ["TT", "FF"]
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Not_In ["x", "a", "dd"]) "TT" "FF") "Z" . at "Z" . to_vector . should_equal ["FF", "TT"]
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Is_In []) "TT" "FF") "Z" . at "Z" . to_vector . should_equal ["FF", "FF"]
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Not_In []) "TT" "FF") "Z" . at "Z" . to_vector . should_equal ["TT", "TT"]
# Passing a column does not work row-by-row, but looks at whole column contents.
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Is_In (t2.at "B")) "TT" "FF") "Z" . at "Z" . to_vector . should_equal ["FF", "TT"]
t3 = table_builder [["x", ["e", "e", "a"]]]
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Is_In (t3.at "x")) "TT" "FF") "Z" . at "Z" . to_vector . should_equal ["TT", "FF"]
# Thus, passing a Column_Ref into Is_In/Not_In is not allowed as it would be confusing.
# Thus, passing a Column_Ref into Is_In is not allowed as it would be confusing.
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Is_In (Column_Ref.Name "B")) "TT" "FF") . should_fail_with Illegal_Argument
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Not_In (Column_Ref.Name "B")) "TT" "FF") . should_fail_with Illegal_Argument
t2.set (Column_Operation.If (Column_Ref.Name "A") (Filter_Condition.Not_In [Column_Ref.Name "B", "X"]) "TT" "FF") . should_fail_with Illegal_Argument
group_builder.specify "text" <|
t = table_builder [["A", [" a ", "b"]], ["B", ["c", " d "]]]

View File

@ -49,19 +49,29 @@ add_specs suite_builder setup =
group_builder.specify "by integer comparisons" <|
t = table_builder [["ix", [1, 2, 3, 4, 5]], ["X", [100, 3, Nothing, 4, 12]], ["Y", [100, 4, 2, Nothing, 11]]]
t1 = t.filter "X" (Filter_Condition.Less than=10)
t1.at "ix" . to_vector . should_equal [2, 4]
t1.at "X" . to_vector . should_equal [3, 4]
t.filter "X" (Filter_Condition.Less than=10) . at "X" . to_vector . should_equal [3, 4]
t.filter "X" (Filter_Condition.Less than=4) . at "X" . to_vector . should_equal [3]
t.filter "X" (Filter_Condition.Less than=4 action=Filter_Action.Remove) . at "X" . to_vector . should_equal [100, Nothing, 4, 12]
t.filter "X" (Filter_Condition.Equal_Or_Less than=4) . at "X" . to_vector . should_equal [3, 4]
t.filter "X" (Filter_Condition.Equal_Or_Less than=4 action=Filter_Action.Remove) . at "X" . to_vector . should_equal [100, Nothing, 12]
t.filter "X" (Filter_Condition.Greater than=4) . at "X" . to_vector . should_equal [100, 12]
t.filter "X" (Filter_Condition.Greater than=4 action=Filter_Action.Remove) . at "X" . to_vector . should_equal [3, Nothing, 4]
t.filter "X" (Filter_Condition.Equal_Or_Greater than=4) . at "X" . to_vector . should_equal [100, 4, 12]
t.filter "X" (Filter_Condition.Equal_Or_Greater than=4 action=Filter_Action.Remove) . at "X" . to_vector . should_equal [3, Nothing]
t.filter "X" (Filter_Condition.Between 4 100) . at "X" . to_vector . should_equal [100, 4, 12]
t2 = t.filter "X" (Filter_Condition.Equal to=100)
t2 . at "X" . to_vector . should_equal [100]
t2 . at "ix" . to_vector . should_equal [1]
t.filter "X" (Filter_Condition.Between 4 100 action=Filter_Action.Remove) . at "X" . to_vector . should_equal [3, Nothing]
t.filter "X" (Filter_Condition.Equal to=100) . at "X" . to_vector . should_equal [100]
t.filter "X" (Filter_Condition.Equal to=123) . at "X" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Equal to=3 action=Filter_Action.Remove) . at "X" . to_vector . should_equal [100, Nothing, 4, 12]
t.filter "X" (Filter_Condition.Is_Finite) . at "ix" . to_vector . should_equal [1, 2, 4, 5]
t.filter "X" (Filter_Condition.Is_Finite action=Filter_Action.Remove) . at "ix" . to_vector . should_equal [3]
t.filter "X" (Filter_Condition.Is_Infinite) . at "ix" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Is_Infinite action=Filter_Action.Remove) . at "ix" . to_vector . should_equal [1, 2, 3, 4, 5]
if test_selection.is_nan_and_nothing_distinct then
t.filter "X" (Filter_Condition.Is_Nan) . at "ix" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Is_Nan action=Filter_Action.Remove) . at "ix" . to_vector . should_equal [1, 2, 3, 4, 5]
v = t.filter "X" (Filter_Condition.Equal to="SOME TEXT :)") . at "X" . to_vector
## We do not do typechecking at Enso level here, as it is
DB-dependent if such mixing is allowed, so we will rely on an SQL
@ -71,25 +81,26 @@ add_specs suite_builder setup =
False -> v.should_fail_with SQL_Error
t.filter "X" (Filter_Condition.Equal to=(t.at "Y")) . at "X" . to_vector . should_equal [100]
t.filter "X" (Filter_Condition.Equal to=(t.at "Y") action=Filter_Action.Remove) . at "X" . to_vector . should_equal [3, Nothing, 4, 12]
t.filter "X" (Filter_Condition.Less than=(t.at "Y")) . at "X" . to_vector . should_equal [3]
t.filter "X" (Filter_Condition.Less than=(t.at "Y") action=Filter_Action.Remove) . at "X" . to_vector . should_equal [100, Nothing, 4, 12]
t.filter "X" (Filter_Condition.Equal_Or_Less than=(t.at "Y")) . at "X" . to_vector . should_equal [100, 3]
t.filter "X" (Filter_Condition.Equal_Or_Less than=(t.at "Y") action=Filter_Action.Remove) . at "X" . to_vector . should_equal [Nothing, 4, 12]
t.filter "X" (Filter_Condition.Equal_Or_Greater than=(t.at "Y")) . at "X" . to_vector . should_equal [100, 12]
t.filter "X" (Filter_Condition.Equal_Or_Greater than=(t.at "Y") action=Filter_Action.Remove) . at "X" . to_vector . should_equal [3, Nothing, 4]
t.filter "X" (Filter_Condition.Greater than=(t.at "Y")) . at "X" . to_vector . should_equal [12]
t.filter "X" (Filter_Condition.Greater than=(t.at "Y") action=Filter_Action.Remove) . at "X" . to_vector . should_equal [100, 3, Nothing, 4]
t.filter "Y" (Filter_Condition.Between (t.at "ix") 100) . at "Y" . to_vector . should_equal [100, 4, 11]
t.filter "Y" (Filter_Condition.Between (t.at "ix") 100 action=Filter_Action.Remove) . at "Y" . to_vector . should_equal [2, Nothing]
t.filter "X" (Filter_Condition.Equal to=(Column_Ref.Name "Y")) . at "X" . to_vector . should_equal [100]
t.filter "X" (Filter_Condition.Equal to=(Column_Ref.Expression "[Y]-1")) . at "X" . to_vector . should_equal [3]
t.filter "X" (Filter_Condition.Less than=(Column_Ref.Name "Y")) . at "X" . to_vector . should_equal [3]
t.filter "X" (Filter_Condition.Equal_Or_Less than=(Column_Ref.Name "Y")) . at "X" . to_vector . should_equal [100, 3]
t.filter "X" (Filter_Condition.Equal_Or_Greater than=(Column_Ref.Name "Y")) . at "X" . to_vector . should_equal [100, 12]
t.filter "X" (Filter_Condition.Greater than=(Column_Ref.Name "Y")) . at "X" . to_vector . should_equal [12]
t.filter "Y" (Filter_Condition.Between (Column_Ref.Name "ix") 100) . at "Y" . to_vector . should_equal [100, 4, 11]
t.filter "X" (Filter_Condition.Is_Finite) . at "ix" . to_vector . should_equal [1, 2, 4, 5]
t.filter "X" (Filter_Condition.Is_Infinite) . at "ix" . to_vector . should_equal []
if test_selection.is_nan_and_nothing_distinct then
t.filter "X" (Filter_Condition.Is_Nan) . at "ix" . to_vector . should_equal []
group_builder.specify "by float operations" <|
t = table_builder [["ix", [1, 2, 3, 4, 5, 6]], ["X", [100.0, 2.5, Nothing, Number.nan, Number.positive_infinity, Number.negative_infinity]]]
@ -109,13 +120,14 @@ add_specs suite_builder setup =
if test_selection.is_nan_and_nothing_distinct then
t.filter "X" Filter_Condition.Is_Nan . at "ix" . to_vector . should_equal [4]
group_builder.specify "Not_Equal test cases" pending="Specification needs clarifying, see: https://github.com/enso-org/enso/issues/5241#issuecomment-1480167927" <|
group_builder.specify "Not_Equal test cases" <|
t = table_builder [["ix", [1, 2, 3, 4, 5]], ["X", [100, 3, Nothing, 4, 12]], ["Y", [100, 4, 2, Nothing, 11]]]
t3 = t.filter "X" (Filter_Condition.Not_Equal to=100)
t3 . at "X" . to_vector . should_equal [3, Nothing, 4, 12]
t3 . at "ix" . to_vector . should_equal [2, 3, 4, 5]
t.filter "X" (Filter_Condition.Not_Equal to=(t.at "Y")) . at "X" . to_vector . should_equal [3, Nothing, 4, 12]
t.filter "X" (Filter_Condition.Not_Equal to=(Column_Ref.Name "Y")) . at "X" . to_vector . should_equal [3, Nothing, 4, 12]
t3 . at "X" . to_vector . should_equal [3, 4, 12]
t3 . at "ix" . to_vector . should_equal [2, 4, 5]
t.filter "X" (Filter_Condition.Not_Equal to=(t.at "Y")) . at "X" . to_vector . should_equal [3, 12]
t.filter "X" (Filter_Condition.Not_Equal to=(Column_Ref.Name "Y")) . at "X" . to_vector . should_equal [3, 12]
t.filter "X" (Filter_Condition.Not_Equal to=(Column_Ref.Expression "[Y]")) . at "X" . to_vector . should_equal [3, 12]
group_builder.specify "by text comparisons" <|
t = table_builder [["ix", [1, 2, 3, 4, 5]], ["X", ["abb", "baca", "b", Nothing, "c"]], ["Y", ["a", "b", "b", "c", "c"]]]
@ -155,19 +167,20 @@ add_specs suite_builder setup =
t = table_builder [["ix", [1, 2, 3, 4, 5]], ["X", ["abb", "bacb", "banana", Nothing, "nana"]], ["Y", ["a", "B", "d", "c", "a"]], ["Z", ["aaaaa", "bbbbb", "[ab]", "[ab]aaaa", "[ab]ccc"]]]
t.filter "X" (Filter_Condition.Starts_With "ba") . at "X" . to_vector . should_equal ["bacb", "banana"]
t.filter "X" (Filter_Condition.Starts_With "ba" action=Filter_Action.Remove) . at "X" . to_vector . should_equal ["abb", Nothing, "nana"]
t.filter "X" (Filter_Condition.Starts_With "BA" Case_Sensitivity.Sensitive) . at "X" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Starts_With "BA" Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["bacb", "banana"]
t.filter "X" (Filter_Condition.Ends_With "na") . at "X" . to_vector . should_equal ["banana", "nana"]
t.filter "X" (Filter_Condition.Ends_With "na" action=Filter_Action.Remove) . at "X" . to_vector . should_equal ["abb", "bacb", Nothing]
t.filter "X" (Filter_Condition.Ends_With "NA" Case_Sensitivity.Sensitive) . at "X" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Ends_With "NA" Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["banana", "nana"]
t.filter "X" (Filter_Condition.Contains "ac") . at "X" . to_vector . should_equal ["bacb"]
t.filter "X" (Filter_Condition.Contains "ac" action=Filter_Action.Remove) . at "X" . to_vector . should_equal ["abb", "banana", Nothing, "nana"]
t.filter "X" (Filter_Condition.Contains "AC" Case_Sensitivity.Sensitive) . at "X" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Contains "AC" Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["bacb"]
t.filter "X" (Filter_Condition.Not_Contains "ac") . at "X" . to_vector . should_equal ["abb", "banana", "nana"]
t.filter "X" (Filter_Condition.Not_Contains "AC" Case_Sensitivity.Sensitive) . at "X" . to_vector . should_equal ["abb", "bacb", "banana", "nana"]
t.filter "X" (Filter_Condition.Not_Contains "AC" Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["abb", "banana", "nana"]
t.filter "X" (Filter_Condition.Starts_With (t.at "Y")) . at "X" . to_vector . should_equal ["abb"]
t.filter "X" (Filter_Condition.Starts_With (t.at "Y") action=Filter_Action.Remove) . at "X" . to_vector . should_equal ["bacb", "banana", Nothing, "nana"]
t.filter "X" (Filter_Condition.Starts_With (t.at "Y") Case_Sensitivity.Sensitive) . at "X" . to_vector . should_equal ["abb"]
t.filter "X" (Filter_Condition.Starts_With (t.at "Y") Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["abb", "bacb"]
t.filter "X" (Filter_Condition.Ends_With (t.at "Y")) . at "X" . to_vector . should_equal ["nana"]
@ -176,27 +189,21 @@ add_specs suite_builder setup =
t.filter "X" (Filter_Condition.Contains (t.at "Y")) . at "X" . to_vector . should_equal ["abb", "nana"]
t.filter "X" (Filter_Condition.Contains (t.at "Y") Case_Sensitivity.Sensitive) . at "X" . to_vector . should_equal ["abb", "nana"]
t.filter "X" (Filter_Condition.Contains (t.at "Y") Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["abb", "bacb", "nana"]
t.filter "X" (Filter_Condition.Not_Contains (t.at "Y")) . at "X" . to_vector . should_equal ["bacb", "banana"]
t.filter "X" (Filter_Condition.Not_Contains (t.at "Y") Case_Sensitivity.Sensitive) . at "X" . to_vector . should_equal ["bacb", "banana"]
t.filter "X" (Filter_Condition.Not_Contains (t.at "Y") Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["banana"]
t.filter "X" (Filter_Condition.Starts_With (Column_Ref.Name "Y")) . at "X" . to_vector . should_equal ["abb"]
t.filter "X" (Filter_Condition.Ends_With (Column_Ref.Name "Y") Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["bacb", "nana"]
t.filter "X" (Filter_Condition.Contains (Column_Ref.Name "Y") Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["abb", "bacb", "nana"]
t.filter "X" (Filter_Condition.Not_Contains (Column_Ref.Name "Y") Case_Sensitivity.Insensitive) . at "X" . to_vector . should_equal ["banana"]
group_builder.specify "by text search (like, not_like)" <|
t = table_builder [["ix", [1, 2, 3, 4, 5]], ["X", ["abb", "bacb", "banana", Nothing, "nana"]], ["Y", ["a", "B", "d", "c", "a"]], ["Z", ["aaaaa", "bbbbb", "[ab]", "[ab]aaaa", "[ab]ccc"]]]
t.filter "X" (Filter_Condition.Like "%an%") . at "X" . to_vector . should_equal ["banana", "nana"]
t.filter "X" (Filter_Condition.Like "%an%" action=Filter_Action.Remove) . at "X" . to_vector . should_equal ["abb", "bacb", Nothing]
t.filter "X" (Filter_Condition.Like "_a%") . at "X" . to_vector . should_equal ["bacb", "banana", "nana"]
t.filter "X" (Filter_Condition.Like "%b") . at "X" . to_vector . should_equal ["abb", "bacb"]
t.filter "X" (Filter_Condition.Like "nana") . at "X" . to_vector . should_equal ["nana"]
t.filter "Z" (Filter_Condition.Like "[ab]_%") . at "Z" . to_vector . should_equal ["[ab]aaaa", "[ab]ccc"]
t.filter "X" (Filter_Condition.Not_Like "%b") . at "X" . to_vector . should_equal ["banana", "nana"]
t.filter "Z" (Filter_Condition.Not_Like "[ab]%") . at "Z" . to_vector . should_equal ["aaaaa", "bbbbb"]
group_builder.specify "text operations should also match newlines" <|
t = table_builder [["X", ['a\n\n\n', 'a\n', 'a\n\n\nb', 'a\nb', 'caa\nbb']]]
t.filter "X" (Filter_Condition.Like 'a_') . at "X" . to_vector . should_equal ['a\n']
@ -208,7 +215,6 @@ add_specs suite_builder setup =
t.filter "X" (Filter_Condition.Ends_With '\nb') . at "X" . to_vector . should_equal ['a\n\n\nb', 'a\nb']
t.filter "X" (Filter_Condition.Ends_With '\n') . at "X" . to_vector . should_equal ['a\n\n\n', 'a\n']
t.filter "X" (Filter_Condition.Starts_With 'c') . at "X" . to_vector . should_equal ['caa\nbb']
t.filter "X" (Filter_Condition.Not_Contains '\nb') . at "X" . to_vector . should_equal ['a\n\n\n', 'a\n']
if test_selection.supports_unicode_normalization then
t = table_builder [["X", ['śnieg', 's\u0301nieg', 'X', Nothing, 'połać', 'połac\u0301']]]
@ -217,7 +223,6 @@ add_specs suite_builder setup =
t.filter "X" (Filter_Condition.Contains 'ś') . at "X" . to_vector . should_equal ['śnieg', 's\u0301nieg']
t.filter "X" (Filter_Condition.Ends_With 'ś') . at "X" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Ends_With 'ć') . at "X" . to_vector . should_equal ['połać', 'połac\u0301']
t.filter "X" (Filter_Condition.Not_Contains 'ś') . at "X" . to_vector . should_equal ['X', 'połać', 'połac\u0301']
# This should be replaced with the disabled test below, once the related bug is fixed.
t.filter "X" (Filter_Condition.Like 'ś%') . at "X" . to_vector . should_equal ['śnieg']
@ -242,24 +247,18 @@ add_specs suite_builder setup =
check_problem (t.filter "X" (Filter_Condition.Ends_With (t.at "ix")))
check_problem (t.filter "X" (Filter_Condition.Contains (t.at "ix")))
check_problem (t.filter "X" (Filter_Condition.Like (t.at "ix")))
check_problem (t.filter "X" (Filter_Condition.Not_Like (t.at "ix")))
check_problem (t.filter "X" (Filter_Condition.Not_Contains (t.at "ix")))
check_problem (t.filter "X" (Filter_Condition.Equal_Ignore_Case (Column_Ref.Name "ix")))
check_problem (t.filter "X" (Filter_Condition.Starts_With (Column_Ref.Name "ix")))
check_problem (t.filter "X" (Filter_Condition.Ends_With (Column_Ref.Name "ix")))
check_problem (t.filter "X" (Filter_Condition.Contains (Column_Ref.Name "ix")))
check_problem (t.filter "X" (Filter_Condition.Like (Column_Ref.Name "ix")))
check_problem (t.filter "X" (Filter_Condition.Not_Like (Column_Ref.Name "ix")))
check_problem (t.filter "X" (Filter_Condition.Not_Contains (Column_Ref.Name "ix")))
check_problem (t.filter "ix" (Filter_Condition.Equal_Ignore_Case "A"))
check_problem (t.filter "ix" (Filter_Condition.Starts_With "A"))
check_problem (t.filter "ix" (Filter_Condition.Ends_With "A"))
check_problem (t.filter "ix" (Filter_Condition.Contains "A"))
check_problem (t.filter "ix" (Filter_Condition.Like "A"))
check_problem (t.filter "ix" (Filter_Condition.Not_Like "A"))
check_problem (t.filter "ix" (Filter_Condition.Not_Contains "A"))
check_problem (t.filter "ix" Filter_Condition.Is_Empty)
check_problem (t.filter "ix" Filter_Condition.Not_Empty)
@ -271,8 +270,6 @@ add_specs suite_builder setup =
check_scalar_type_error_handling (t.filter "X" (Filter_Condition.Ends_With 42))
check_scalar_type_error_handling (t.filter "X" (Filter_Condition.Contains 42))
check_scalar_type_error_handling (t.filter "X" (Filter_Condition.Like 42))
check_scalar_type_error_handling (t.filter "X" (Filter_Condition.Not_Like 42))
check_scalar_type_error_handling (t.filter "X" (Filter_Condition.Not_Contains 42))
group_builder.specify "by nulls" <|
t = table_builder [["ix", [1, 2, 3, 4]], ["X", [Nothing, 1, Nothing, 4]]]
@ -287,17 +284,11 @@ add_specs suite_builder setup =
group_builder.specify "by an Is_In check" <|
t = table_builder [["ix", [1, 2, 3, Nothing, 5, 6]], ["X", ["a", "b", "ccc", "X", "f", "2"]]]
t1 = table_builder [["txt", ["X", "a", "c", Nothing]], ["int", [Nothing, 2, 5, 4]], ["bool", [True, Nothing, Nothing, True]]]
t2 = table_builder [["txt", ["X", "a", "c", "q"]], ["int", [123, 2, 5, 4]], ["bool", [True, True, True, True]]]
t.filter "X" (Filter_Condition.Is_In (t1.at "txt")) . at "X" . to_vector . should_equal ["a", "X"]
t.filter "X" (Filter_Condition.Is_In (t1.at "txt" . to_vector)) . at "X" . to_vector . should_equal ["a", "X"]
t.filter "X" (Filter_Condition.Not_In (t1.at "txt")) . at "X" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Not_In (t2.at "txt")) . at "X" . to_vector . should_equal ["b", "ccc", "f", "2"]
t.filter "X" (Filter_Condition.Not_In (t1.at "txt" . to_vector)) . at "X" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Not_In (t2.at "txt" . to_vector)) . at "X" . to_vector . should_equal ["b", "ccc", "f", "2"]
t.filter "X" (Filter_Condition.Is_In ["ccc"]) . at "X" . to_vector . should_equal ["ccc"]
t.filter "X" (Filter_Condition.Is_In []) . at "X" . to_vector . should_equal []
t.filter "X" (Filter_Condition.Not_In []) . at "X" . to_vector . should_equal ["a", "b", "ccc", "X", "f", "2"]
if test_selection.allows_mixed_type_comparisons then
mixed = t.filter "X" (Filter_Condition.Is_In (t1.at "int")) . at "X" . to_vector
@ -308,8 +299,6 @@ add_specs suite_builder setup =
t.filter "ix" (Filter_Condition.Is_In (t1.at "int" . to_vector)) . at "ix" . to_vector . should_equal [2, 5]
t.filter "ix" (Filter_Condition.Is_In [2, 5, 4]) . at "ix" . to_vector . should_equal [2, 5]
t.filter "ix" (Filter_Condition.Is_In [Nothing]) . at "ix" . to_vector . should_equal []
t.filter "ix" (Filter_Condition.Not_In [Nothing]) . at "ix" . to_vector . should_equal []
t.filter "ix" (Filter_Condition.Not_In [1, 3]) . at "ix" . to_vector . should_equal [2, 5, 6]
v1 = t.filter "X" (Filter_Condition.Is_In ["c", "f", "b", "b", "b", 15, Nothing]) . at "X" . to_vector
case test_selection.allows_mixed_type_comparisons of
@ -329,7 +318,7 @@ add_specs suite_builder setup =
t3.filter "B" (Filter_Condition.Is_In [False]) . at "B" . to_vector . should_equal [False, False, False]
t3.filter "C" (Filter_Condition.Is_In [False, False]) . at "C" . to_vector . should_equal [False]
group_builder.specify "does not allow Column_Ref in Is_In/Not_In because that would be confusing" <|
group_builder.specify "does not allow Column_Ref in Is_In because that would be confusing" <|
## Is In and Not In check if a value is contained anywhere in a provided collection (e.g. column),
NOT on a row-by-row basis like all other operations. Column_Ref is used with row-by-row ops,
so this would only cause confusion. Very rarely someone wants to filter a column by Is_In
@ -459,7 +448,6 @@ add_specs suite_builder setup =
+ [Filter_Condition.Less Nothing, Filter_Condition.Equal_Or_Less Nothing]
+ [Filter_Condition.Equal_Or_Greater Nothing, Filter_Condition.Greater Nothing]
+ [Filter_Condition.Between Nothing Nothing , Filter_Condition.Is_In [Nothing]]
+ [Filter_Condition.Not_In [Nothing]]
+ [Filter_Condition.Is_In [1, Nothing, 2]]
fcs.map fc->
Test.with_clue fc.to_text <|

View File

@ -75,7 +75,7 @@ add_specs suite_builder setup =
## TODO currently our builder does not allow all-null tables, so we
create one with a 0 and remove it by filter. See #6159.
t0 = table_builder [["X", [0, Nothing, Nothing, Nothing]]]
t1 = t0.filter "X" (Filter_Condition.Is_Nothing)
t1 = t0.filter "X" Filter_Condition.Is_Nothing
t1.row_count . should_equal 3
t1.at "X" . to_vector . should_equal [Nothing, Nothing, Nothing]
@ -171,7 +171,7 @@ add_specs suite_builder setup =
group_builder.specify "select_blank_columns and remove_blank_columns should deal with edge cases" <|
t = table_builder [["X", [1, 2, 3, 4]]]
no_rows = t.filter "X" (Filter_Condition.Equal to=0)
no_rows = t.filter "X" Filter_Condition.Is_Nothing
no_rows.row_count . should_equal 0
no_rows.at "X" . to_vector . should_equal []

View File

@ -42,14 +42,17 @@ add_specs suite_builder =
group_builder.teardown <|
data.teardown
group_builder.specify "should serialize Tables and Columns to their SQL representation" pending="ToDo: decide on how we handle ==, see https://github.com/enso-org/enso/issues/5241" <|
group_builder.specify "should serialize Tables and Columns to their SQL representation" <|
q1 = data.t1.filter (data.t1.at "A" == 42) . to_json
part1 = JS_Object.from_pairs [["sql_code", 'SELECT "T1"."A" AS "A", "T1"."B" AS "B", "T1"."C" AS "C" FROM "T1" AS "T1" WHERE ("T1"."A" = ']]
part1 = JS_Object.from_pairs [["sql_code", 'SELECT "T1"."A" AS "A", "T1"."B" AS "B", "T1"."C" AS "C" FROM "T1" AS "T1" WHERE (("T1"."A") = (']]
part2_sub = JS_Object.from_pairs [["value", 42]]
part2 = JS_Object.from_pairs [["sql_interpolation", part2_sub]]
part3 = JS_Object.from_pairs [["sql_code", ")"]]
expected = JS_Object.from_pairs [["query", [part1, part2, part3]]] . to_text
part3 = JS_Object.from_pairs [["sql_code", ")) = ("]]
part4_sub = JS_Object.from_pairs [["value", True]]
part4 = JS_Object.from_pairs [["sql_interpolation", part4_sub]]
part5 = JS_Object.from_pairs [["sql_code", ")"]]
expected = JS_Object.from_pairs [["query", [part1, part2, part3, part4, part5]]] . to_text
q1.should_equal expected
q2 = data.t1.at "A" . to_json
@ -90,6 +93,9 @@ add_specs suite_builder =
t2 = data.t1.filter "A" (Filter_Condition.Between 10 20)
t2.to_sql.prepare . should_equal ['SELECT "T1"."A" AS "A", "T1"."B" AS "B", "T1"."C" AS "C" FROM "T1" AS "T1" WHERE ("T1"."A" BETWEEN ? AND ?)', [10, 20]]
t2r = data.t1.filter "A" (Filter_Condition.Between 10 20 Filter_Action.Remove)
t2r.to_sql.prepare . should_equal ['SELECT "T1"."A" AS "A", "T1"."B" AS "B", "T1"."C" AS "C" FROM "T1" AS "T1" WHERE (NOT CAST(COALESCE(("T1"."A" BETWEEN ? AND ?), ?) AS BOOLEAN))', [10, 20, False]]
group_builder.specify "should generate an IN expression" <|
t2 = data.t1.filter "A" (Filter_Condition.Is_In [1, 2, 'foo'])
t2.to_sql.prepare . should_equal ['SELECT "T1"."A" AS "A", "T1"."B" AS "B", "T1"."C" AS "C" FROM "T1" AS "T1" WHERE "T1"."A" IN (?, ?, ?)', [1, 2, "foo"]]

View File

@ -4,7 +4,6 @@ from Standard.Table import Table
import Standard.Examples
import Standard.Visualization.Table as Table_Visualization
import Standard.Visualization.Preprocessor as Preprocessor
from Standard.Test_New import all

View File

@ -3,7 +3,6 @@ from Standard.Visualization import all
import Standard.Examples
import Standard.Visualization.Text as TextVis
import Standard.Visualization.Preprocessor as Preprocessor
from Standard.Test_New import all

View File

@ -10,7 +10,7 @@ main =
operator14 = var1.to_column "Color"
operator16 = operator14.to_table
operator40 = enso_project.data/'benchmarks-vectorized-3-2023-07-17.txt' . read (Delimited_Format.Delimited)
operator1 = operator40.filter 'Column 1' (Filter_Condition.Not_Contains "average")
operator1 = operator40.filter 'Column 1' (Filter_Condition.Contains "average" Filter_Action.Remove)
operator3 = operator1.parse_to_columns 'Column 1' text1
operator4 = operator3.select_columns ['Operation']
operator5 = operator4.distinct