Some little bits from Book Club week 1 (#4058)

- Add `get` to Table.
- Correct `Count Nothing` examples.
- Add `join` to File.
- Add `File_Format.all` listing all installed formats.
- Add some more ALIAS entries.
This commit is contained in:
James Dunkerley 2023-01-18 11:46:13 +00:00 committed by GitHub
parent 4ea1880dec
commit 48e5ed9eea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 156 additions and 31 deletions

View File

@ -3,10 +3,11 @@ import project.Data.Pair.Pair
import project.Data.Text.Encoding.Encoding
import project.Data.Text.Text
import project.Data.Vector.Vector
import project.Error.Common.Type_Error
import project.Error.Error
import project.Error.File_Error.File_Error
import project.Error.Illegal_Argument.Illegal_Argument
import project.Error.Problem_Behavior.Problem_Behavior
import project.Meta
import project.Network.URI.URI
import project.Network.HTTP.Header.Header
import project.Network.HTTP.HTTP
@ -20,7 +21,10 @@ import project.System.File_Format.File_Format
from project.Data.Boolean import Boolean, True, False
## Read a file using the specified file format
## ALIAS Load, Open
Read's a file into Enso.
Uses the specified file format to parse the file into an Enso type. If not
specified will use the file's extension to determine the file format.
Arguments:
- path: The path to the file to read.
@ -28,6 +32,8 @@ from project.Data.Boolean import Boolean, True, False
If `Auto_Detect` is specified; the provided file determines the specific
type and configures it appropriately. If there is no matching type then
a `File_Error.Unsupported_Type` error is returned.
You can use `File_Format.all` to get a list of currently loaded
formats.
- on_problems: Specifies the behavior when a problem occurs during the
function.
By default, a warning is issued, but the operation proceeds.
@ -54,7 +60,8 @@ read : File_Format -> Problem_Behavior -> Any ! File_Error
read path format=Auto_Detect (on_problems=Problem_Behavior.Report_Warning) =
File.new path . read format on_problems
## Open and read the file at the provided `path`.
## ALIAS Load Text, Open Text
Open and read the file at the provided `path`.
Arguments:
- path: The path of the file to open and read the contents of. It will
@ -146,7 +153,7 @@ fetch uri method=HTTP_Method.Get headers=[] parse=True =
_ : Vector -> Header.new (h.at 0) (h.at 1)
_ : Pair -> Header.new (h.at 0) (h.at 1)
_ : Header -> h
_ -> Error.throw (Type_Error.Error Header "Invalid header type (must be a Pair, Vector or Header).")
_ -> Error.throw (Illegal_Argument.Error "Invalid header type - all values must be Vector, Pair or Header (got "+(Meta.get_simple_type_name h)+").")
request = Request.new method uri parsed_headers
response = HTTP.new.request request

View File

@ -159,7 +159,8 @@ type File
with_input_stream self open_options action =
Managed_Resource.bracket (self.new_input_stream open_options) (_.close) action
## Read a file using the specified file format
## ALIAS Load, Open
Read a file using the specified file format
Arguments:
- format: A `File_Format` object used to read file into memory.
@ -192,7 +193,8 @@ type File
read self format=Auto_Detect (on_problems=Problem_Behavior.Report_Warning) =
format.read self on_problems
## Reads all bytes in this file into a byte vector.
## ALIAS Load Bytes, Open Bytes
Reads all bytes in this file into a byte vector.
> Example
Read all of the bytes in the file.
@ -205,7 +207,8 @@ type File
opts = [File_Access.Read]
self.with_input_stream opts (_.read_all_bytes)
## Reads the whole file into a `Text`, with specified encoding.
## ALIAS Load Text, Open Text
Reads the whole file into a `Text`, with specified encoding.
Arguments:
- encoding: The text encoding to decode the file with. Defaults to UTF-8.
@ -241,6 +244,31 @@ type File
/ : (Text | File) -> File
/ self subpath = self.resolve subpath
## Join two or more path segments together, normalizing the `..` and `.` subpaths.
Arguments:
- subpaths: The path segment or segments to join to the path of `self`.
> Example
Concatenate two file path segments.
import Standard.Examples
example_append = Examples.data_dir.join "scratch_file"
> Example
Concatenate multiple file path segments and normalizes the result.
import Standard.Examples
example_append = Examples.data_dir.join ["2022", "10", "31", "scratch_file"]
join : (Text | File | Vector) -> File
join self subpaths = case subpaths of
_ : Vector -> (subpaths.fold self c->p-> c / p) . normalize
_ -> self.join [subpaths]
## PRIVATE
Internal method to join two path segments together.
resolve : (Text | File) -> File
resolve self = @Builtin_Method "File.resolve"

View File

@ -55,6 +55,14 @@ type Auto_Detect
type File_Format
## Gets all the currently available file formats.
The available file formats are ones provided by libraries which are
imported within the current project. Importing an new library may cause
more entries to show up on this list.
all : Vector
all = [Auto_Detect] + format_types
## PRIVATE
Implements the `File.read` for this `File_Format`
read : File -> Problem_Behavior -> Any

View File

@ -101,9 +101,20 @@ type Table
at : Text | Integer -> Column ! No_Such_Column | Index_Out_Of_Bounds
at self selector=0 = case selector of
_ : Integer -> self.make_column (self.internal_columns.at selector)
_ : Text ->
internal_column = self.internal_columns.find if_missing=(Error.throw (No_Such_Column.Error selector)) (p -> p.name == selector)
self.make_column internal_column
_ -> self.get selector (Error.throw (No_Such_Column.Error selector))
## Returns the column with the given name or index.
Arguments:
- selector: The name or index of the column being looked up.
- if_missing: The value to use if the selector isn't present.
get : Text | Integer -> Any -> Column | Any
get self selector=0 ~if_missing=Nothing =
internal_column = case selector of
_ : Integer -> self.internal_columns.get selector if_missing=Nothing
_ : Text -> self.internal_columns.find (p -> p.name == selector) if_missing=Nothing
_ -> Error.throw (Illegal_Argument.Error "expected 'selector' to be either a Text or an Integer, but got "+(Meta.get_simple_type_name selector)+".")
if internal_column.is_nothing then if_missing else self.make_column internal_column
## Returns the number of columns in the table.
column_count : Integer
@ -497,6 +508,7 @@ type Table
self.updated_context new_ctx
## UNSTABLE
ALIAS Add Column, Update Column
Sets the column value at the given name.
@ -893,7 +905,7 @@ type Table
> Example
Group by the Key column, count the rows
table.aggregate [Aggregate_Column.Group_By "Key", Aggregate_Column.Count Nothing]
table.aggregate [Aggregate_Column.Group_By "Key", Aggregate_Column.Count]
aggregate : Vector Aggregate_Column -> Problem_Behavior -> Table
aggregate self columns (on_problems=Report_Warning) =
validated = Aggregate_Column_Helper.prepare_aggregate_columns columns self

View File

@ -202,10 +202,34 @@ type Table
_ : Integer ->
java_columns = Vector.from_polyglot_array self.java_table.getColumns
Column.Value (java_columns.at selector)
_ : Text ->
case self.java_table.getColumnByName selector of
Nothing -> Error.throw (No_Such_Column.Error selector)
c -> Column.Value c
_ -> self.get selector (Error.throw (No_Such_Column.Error selector))
## Returns the column with the given name or index.
Arguments:
- selector: The name or index of the column being looked up.
- if_missing: The value to use if the selector isn't present.
> Example
Get the names of all of the items from the shop inventory.
import Standard.Examples
example_at = Examples.inventory_table.get "item_name"
> Example
Get the last column.
import Standard.Examples
example_at = Examples.inventory_table.get -1
get : Text | Integer -> Any -> Column | Any
get self selector=0 ~if_missing=Nothing =
java_column = case selector of
_ : Integer -> Vector.from_polyglot_array self.java_table.getColumns . get selector
_ : Text -> self.java_table.getColumnByName selector
_ -> Error.throw (Illegal_Argument.Error "expected 'selector' to be either a Text or an Integer, but got "+(Meta.get_simple_type_name selector)+".")
if java_column.is_nothing then if_missing else Column.Value java_column
## Returns the number of columns in the table.
column_count : Integer
@ -502,7 +526,7 @@ type Table
> Example
Group by the Key column, count the rows
table.aggregate [Group_By "Key", Count Nothing]
table.aggregate [Aggregate_Column.Group_By "Key", Aggregate_Column.Count]
aggregate : Vector Aggregate_Column -> Problem_Behavior -> Table
aggregate self columns (on_problems=Report_Warning) =
validated = Aggregate_Column_Helper.prepare_aggregate_columns columns self
@ -924,7 +948,7 @@ type Table
drop self range=(First 1) =
Index_Sub_Range_Module.drop_helper self.row_count self.rows.at self.slice (slice_ranges self) range
## ALIAS Add Column
## ALIAS Add Column, Update Column
Sets the column value at the given name.

View File

@ -1,5 +1,6 @@
from Standard.Base import all
import Standard.Base.Error.Common.Type_Error
import Standard.Base.Error.Illegal_Argument.Illegal_Argument
import Standard.Base.Error.Unimplemented.Unimplemented
import project.Data.Table.Table
@ -50,7 +51,7 @@ Table.from_objects value fields=Nothing =
_ : Number -> ["Value"]
_ : Boolean -> ["Value"]
_ : Text -> ["Value"]
_ -> Error.throw (Type_Error.Error Any "Invalid item within Vector for Table.from_objects. Currently only JS_Object, Number, Boolean, Text and Nothing are supported.")
_ -> Error.throw (Illegal_Argument.Error "Invalid item within Vector for Table.from_objects. Currently only JS_Object, Number, Boolean, Text and Nothing are supported (got "+(Meta.get_simple_type_name v)+").")
get_value v field = case v of
_ : JS_Object -> v.get field
@ -87,4 +88,4 @@ Table.from_objects value fields=Nothing =
Table.new (used_fields.zip used_values)
_ : Array -> Table.from_objects (Vector.from_polyglot_array value) fields
_ -> Error.throw (Type_Error.Error Any "Invalid value for Table.from_objects. Currently must be one of JS_Object, Vector, Array, Number, Boolean, Text and Nothing are supported.")
_ -> Error.throw (Illegal_Argument.Error "Invalid value for Table.from_objects. Currently must be one of JS_Object, Vector, Array, Number, Boolean, Text and Nothing are supported (got "+(Meta.get_simple_type_name value)+").")

View File

@ -28,7 +28,7 @@ bench =
IO.println <| "Making table data..."
table = create_table vector_size
Bench.measure (table.aggregate [Count Nothing]) "Count table" iter_size num_iterations
Bench.measure (table.aggregate [Count]) "Count table" iter_size num_iterations
Bench.measure (table.aggregate [Maximum "ValueWithNothing"]) "Max table" iter_size num_iterations
Bench.measure (table.aggregate [Sum "ValueWithNothing"]) "Sum table" iter_size num_iterations
# Bench.measure (table.aggregate [Count_Distinct "Index"]) "Count Distinct table" iter_size num_iterations
@ -36,7 +36,7 @@ bench =
# Bench.measure (table.aggregate [Median "Value"]) "Median table" iter_size num_iterations
# Bench.measure (table.aggregate [Mode "Index"]) "Mode table" iter_size num_iterations
Bench.measure (table.aggregate [Group_By "Index", Count Nothing]) "Count grouped" iter_size num_iterations
Bench.measure (table.aggregate [Group_By "Index", Count]) "Count grouped" iter_size num_iterations
Bench.measure (table.aggregate [Group_By "Index", Maximum "ValueWithNothing"]) "Max table" iter_size num_iterations
Bench.measure (table.aggregate [Group_By "Index", Sum "ValueWithNothing"]) "Sum table" iter_size num_iterations
# Bench.measure (table.aggregate [Group_By "Index", Count_Distinct "Code"]) "Count Distinct grouped" iter_size num_iterations
@ -44,7 +44,7 @@ bench =
# Bench.measure (table.aggregate [Group_By "Index", Median "Value"]) "Median grouped" iter_size num_iterations
# Bench.measure (table.aggregate [Group_By "Index", Mode "Index"]) "Mode grouped" iter_size num_iterations
Bench.measure (table.aggregate [Group_By "Index", Group_By "Flag", Count Nothing]) "Count 2 level groups" iter_size num_iterations
Bench.measure (table.aggregate [Group_By "Index", Group_By "Flag", Count]) "Count 2 level groups" iter_size num_iterations
Bench.measure (table.aggregate [Group_By "Index", Group_By "Flag", Maximum "ValueWithNothing"]) "Max table" iter_size num_iterations
Bench.measure (table.aggregate [Group_By "Index", Group_By "Flag", Sum "ValueWithNothing"]) "Sum table" iter_size num_iterations
# Bench.measure (table.aggregate [Group_By "Index", Group_By "Flag", Count_Distinct "Code"]) "Count Distinct 2 level groups" iter_size num_iterations

View File

@ -41,7 +41,7 @@ spec setup =
Test.group prefix+"Table.aggregate should summarize whole table" <|
Test.specify "should be able to count" <|
grouped = table.aggregate [Count Nothing]
grouped = table.aggregate [Count]
materialized = materialize grouped
grouped.row_count . should_equal 1
materialized.column_count . should_equal 1
@ -194,7 +194,7 @@ spec setup =
Test.group prefix+"Table.aggregate should summarize empty table" <|
Test.specify "should be able to count" <|
grouped = empty_table.aggregate [Count Nothing]
grouped = empty_table.aggregate [Count]
materialized = materialize grouped
grouped.row_count . should_equal 1
materialized.column_count . should_equal 1
@ -307,7 +307,7 @@ spec setup =
Test.group prefix+"Table.aggregate should not summarize empty table when grouped" <|
Test.specify "should be able to count" <|
grouped = empty_table.aggregate [Group_By 0, Count Nothing]
grouped = empty_table.aggregate [Group_By 0, Count]
materialized = materialize grouped
grouped.row_count . should_equal 0
materialized.column_count . should_equal 2
@ -407,7 +407,7 @@ spec setup =
Test.group prefix+"Table.aggregate should be able to group on single field" <|
Test.specify "should be able to count" <|
grouped = table.aggregate [Group_By "Index", Count Nothing]
grouped = table.aggregate [Group_By "Index", Count]
materialized = materialize grouped
grouped.row_count . should_equal 10
materialized.column_count . should_equal 2
@ -597,7 +597,7 @@ spec setup =
Test.group prefix+"Table.aggregate should be able to group on multiple fields not in left columns" <|
Test.specify "should be able to count" <|
grouped = table.aggregate [Group_By "Flag", Count Nothing, Group_By "Index"]
grouped = table.aggregate [Group_By "Flag", Count, Group_By "Index"]
materialized = materialize grouped
grouped.row_count . should_equal 20
materialized.column_count . should_equal 3
@ -1331,7 +1331,7 @@ spec setup =
problems.at 0 . rows . length . should_equal 15
Test.specify "should merge Floating Point Grouping warnings" <|
new_table = table.aggregate [Group_By "Float", Count Nothing]
new_table = table.aggregate [Group_By "Float", Count]
problems = Warning.get_all new_table . map .value
problems.length . should_equal 1
problems.at 0 . is_a Floating_Point_Grouping.Error . should_be_true

View File

@ -1,5 +1,6 @@
from Standard.Base import all
import Standard.Base.Error.Common.Index_Out_Of_Bounds
import Standard.Base.Error.Illegal_Argument.Illegal_Argument
from Standard.Table.Errors import all
@ -30,6 +31,7 @@ spec setup =
column_1.to_vector . should_equal [4, 5, 6]
table.at "nonexistent column name" . should_fail_with No_Such_Column.Error
Test.specify "should allow selecting columns by index" <|
column_1 = table.at
column_1.name . should_equal "foo"
@ -45,6 +47,39 @@ spec setup =
table.at 100 . should_fail_with Index_Out_Of_Bounds.Error
Test.specify "should fail with Type Error is not an Integer or Text" <|
table.at (Pair.new 1 2) . should_fail_with Illegal_Argument.Error
table.at (Pair.new 1 2) . catch . to_display_text . should_equal "Illegal Argument: expected 'selector' to be either a Text or an Integer, but got Pair.Value."
Test.group prefix+"Table.get" <|
Test.specify "should allow selecting columns by name" <|
column_1 = table.get "bar"
column_1.name . should_equal "bar"
column_1.to_vector . should_equal [4, 5, 6]
table.get "nonexistent column name" . should_equal Nothing
table.get "nonexistent column name" column_1 . name . should_equal "bar"
Test.specify "should allow selecting columns by index" <|
column_1 = table.get
column_1.name . should_equal "foo"
column_1.to_vector . should_equal [1, 2, 3]
column_2 = table.get 2
column_2.name . should_equal "Baz"
column_2.to_vector . should_equal [7, 8, 9]
column_3 = table.get -1
column_3.name . should_equal "abcd123"
column_3.to_vector . should_equal [19, 20, 21]
table.get 100 . should_equal Nothing
table.get 100 column_1 . name . should_equal "foo"
Test.specify "should fail with Type Error is not an Integer or Text" <|
table.get (Pair.new 1 2) . should_fail_with Illegal_Argument.Error
table.get (Pair.new 1 2) . catch . to_display_text . should_equal "Illegal Argument: expected 'selector' to be either a Text or an Integer, but got Pair.Value."
Test.group prefix+"Table.column_names" <|
Test.specify "should return the names of all columns" <|
table.column_names . should_equal ["foo", "bar", "Baz", "foo_1", "foo_2", "ab.+123", "abcd123"]

View File

@ -39,7 +39,7 @@ spec = Test.group "Aggregate Columns" <|
result.should_equal expected_result
Test.specify "should be able to count a set" <|
test_aggregator simple_table (Count Nothing) "Count" simple_table.row_count
test_aggregator simple_table (Count) "Count" simple_table.row_count
test_aggregator simple_table (Count test_name) test_name simple_table.row_count
test_aggregator empty_table (Count test_name) test_name empty_table.row_count

View File

@ -21,6 +21,15 @@ spec =
file = File.new sample_file
file . should_equal sample_file
Test.specify "should allow joining sections" <|
f_1 = File.new "foo/bar"
(File.new "foo" / "bar") . normalize . should_equal f_1
File.new "foo" . join "bar" . should_equal f_1
f_2 = File.new "foo/a/b/c/d/e"
File.new "foo" . join "a" . join "b" . join "c" . join "d" . join "e" . should_equal f_2
File.new "foo" . join ["a", "b", "c", "d", "e"] . should_equal f_2
Test.specify "should check if file exists" <|
non_existent_file.exists.should_be_false
sample_file.exists.should_be_true
@ -41,8 +50,9 @@ spec =
Test.specify "should normalize file" <|
f_1 = File.new "foo"
f_2 = File.new "bar/../baz/../foo"
f_2.normalize.should_equal f_1
File.new "bar/../baz/../foo" . normalize . should_equal f_1
(File.new "bar" / ".." / "baz" / ".." / "foo") . normalize . should_equal f_1
File.new "bar" . join ["..", "baz", "..", "foo"] . should_equal f_1
Test.specify "should handle `==` on files" <|
(File.new "foo").should_equal (File.new "foo")