mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 22:10:15 +03:00
Fix date comparisons and test sorting of tables and vectors with dates (#3745)
Implements https://www.pivotaltracker.com/story/show/183402892 # Important Notes - Fixes inconsistent `compare_to` vs `==` behaviour in date/time types and adds test for that. - Adds test for `Table.order_by` on dates and custom types. - Fixes an issue with `Table.order_by` for custom types. - Unifies how incomparable objects are reported by `Table.order_by` and `Vector.sort`. - Adds benchmarks comparing `Table.order_by` and `Vector.sort` performance.
This commit is contained in:
parent
835ac05218
commit
61a4120cfb
@ -1,4 +1,4 @@
|
||||
from Standard.Base import Boolean, True, False, Nothing, Vector, Number, Any, Error, Array, Panic, Illegal_Argument_Error_Data, Illegal_Argument_Error, Unsupported_Argument_Types, Unsupported_Argument_Types_Data
|
||||
from Standard.Base import Boolean, True, False, Nothing, Vector, Number, Any, Error, Array, Panic, Illegal_Argument_Error_Data, Illegal_Argument_Error, Unsupported_Argument_Types, Unsupported_Argument_Types_Data, Incomparable_Values_Error
|
||||
|
||||
from Standard.Base.Data.Vector import Empty_Error
|
||||
|
||||
@ -130,10 +130,10 @@ compute_bulk data statistics=[Count, Sum] =
|
||||
statistics.map_with_index i->s->case s of
|
||||
Count -> count_min_max_values.count
|
||||
Minimum ->
|
||||
if count_min_max_values.comparatorError then (Error.throw Vector.Incomparable_Values_Error) else
|
||||
if count_min_max_values.comparatorError then (Error.throw Incomparable_Values_Error) else
|
||||
count_min_max_values.minimum
|
||||
Maximum ->
|
||||
if count_min_max_values.comparatorError then (Error.throw Vector.Incomparable_Values_Error) else
|
||||
if count_min_max_values.comparatorError then (Error.throw Incomparable_Values_Error) else
|
||||
count_min_max_values.maximum
|
||||
Covariance s -> calculate_correlation_statistics data s . covariance
|
||||
Pearson s -> calculate_correlation_statistics data s . pearsonCorrelation
|
||||
@ -248,7 +248,7 @@ rank_data input method=Rank_Method.Average =
|
||||
|
||||
report_nullpointer caught_panic = Error.throw (Illegal_Argument_Error_Data caught_panic.payload.cause.getMessage)
|
||||
handle_nullpointer = Panic.catch NullPointerException handler=report_nullpointer
|
||||
handle_classcast = Panic.catch ClassCastException handler=(_ -> Error.throw Vector.Incomparable_Values_Error)
|
||||
handle_classcast = Panic.catch ClassCastException handler=(_ -> Error.throw Incomparable_Values_Error)
|
||||
|
||||
handle_classcast <| handle_nullpointer <|
|
||||
java_ranks = Rank.rank input.to_array Comparator.new java_method
|
||||
|
@ -495,18 +495,19 @@ type Date
|
||||
|
||||
(Date.new 2000).compare_to (Date.new 2001)
|
||||
compare_to : Date -> Ordering
|
||||
compare_to self that =
|
||||
sign = Time_Utils.compare_to_localdate self that
|
||||
Ordering.from_sign sign
|
||||
compare_to self that = case that of
|
||||
Date ->
|
||||
sign = Time_Utils.compare_to_localdate self that
|
||||
Ordering.from_sign sign
|
||||
_ -> Error.throw (Type_Error_Data Date that "that")
|
||||
|
||||
## Compares two Dates for equality.
|
||||
== : Date -> Boolean
|
||||
== self that =
|
||||
case that of
|
||||
Date ->
|
||||
sign = Time_Utils.compare_to_localdate self that
|
||||
0 == sign
|
||||
_ -> False
|
||||
== self that = case that of
|
||||
Date ->
|
||||
sign = Time_Utils.compare_to_localdate self that
|
||||
0 == sign
|
||||
_ -> False
|
||||
|
||||
## PRIVATE
|
||||
week_days_between start end =
|
||||
|
@ -557,16 +557,16 @@ type Date_Time
|
||||
|
||||
(Date_Time.new 2000).compare_to (Date_Time.new 2001)
|
||||
compare_to : Time -> Ordering
|
||||
compare_to self that =
|
||||
sign = Time_Utils.compare_to_zoneddatetime self that
|
||||
Ordering.from_sign sign
|
||||
compare_to self that = case that of
|
||||
Date_Time ->
|
||||
sign = Time_Utils.compare_to_zoneddatetime self that
|
||||
Ordering.from_sign sign
|
||||
_ -> Error.throw (Type_Error_Data Date_Time that "that")
|
||||
|
||||
## Compares two Date_Time for equality.
|
||||
== : Date_Time -> Boolean
|
||||
== self that =
|
||||
case that of
|
||||
Date_Time ->
|
||||
sign = Time_Utils.compare_to_zoneddatetime self that
|
||||
0 == sign
|
||||
_ ->
|
||||
False
|
||||
== self that = case that of
|
||||
Date_Time ->
|
||||
sign = Time_Utils.compare_to_zoneddatetime self that
|
||||
0 == sign
|
||||
_ -> False
|
||||
|
@ -323,16 +323,16 @@ type Time_Of_Day
|
||||
time_2 = Time_Of_Day.new minute=50
|
||||
time_1.compare_to time_2
|
||||
compare_to : Time_Of_Day -> Ordering
|
||||
compare_to self that =
|
||||
sign = Time_Utils.compare_to_localtime self that
|
||||
Ordering.from_sign sign
|
||||
compare_to self that = case that of
|
||||
Time_Of_Day ->
|
||||
sign = Time_Utils.compare_to_localtime self that
|
||||
Ordering.from_sign sign
|
||||
_ -> Error.throw (Type_Error_Data Time_Of_Day that "that")
|
||||
|
||||
## Compares two Time_Of_Day for equality.
|
||||
== : Date -> Boolean
|
||||
== self that =
|
||||
case that of
|
||||
Time_Of_Day ->
|
||||
sign = Time_Utils.compare_to_localtime self that
|
||||
0 == sign
|
||||
_ ->
|
||||
False
|
||||
== self that = case that of
|
||||
Time_Of_Day ->
|
||||
sign = Time_Utils.compare_to_localtime self that
|
||||
0 == sign
|
||||
_ -> False
|
||||
|
@ -5,6 +5,7 @@ import Standard.Base.Runtime.Unsafe
|
||||
|
||||
polyglot java import java.lang.IndexOutOfBoundsException
|
||||
polyglot java import org.enso.base.Array_Builder
|
||||
polyglot java import java.lang.ClassCastException
|
||||
|
||||
## Creates a new vector of the given length, initializing elements using
|
||||
the provided constructor function.
|
||||
@ -858,7 +859,7 @@ type Vector a
|
||||
Sorting a vector of `Pair`s on the first element, descending.
|
||||
|
||||
[Pair 1 2, Pair -1 8].sort (_.first) (order = Sort_Direction.Descending)
|
||||
sort : (Any -> Any) -> (Any -> Any -> Ordering) -> Sort_Direction -> Vector Any
|
||||
sort : (Any -> Any) -> (Any -> Any -> Ordering) -> Sort_Direction -> Vector Any ! Incomparable_Values_Error
|
||||
sort self (on = x -> x) (by = (_.compare_to _)) (order = Sort_Direction.Ascending) =
|
||||
## Prepare the destination array that will underlie the vector. We do
|
||||
not want to sort in place on the original vector, as `sort` is not
|
||||
@ -874,9 +875,9 @@ type Vector a
|
||||
compare = if order == Sort_Direction.Ascending then comp_ascending else
|
||||
comp_descending
|
||||
|
||||
new_vec_arr.sort compare
|
||||
|
||||
Vector.from_polyglot_array new_vec_arr
|
||||
handle_incomparable_value <|
|
||||
new_vec_arr.sort compare
|
||||
Vector.from_polyglot_array new_vec_arr
|
||||
|
||||
## UNSTABLE
|
||||
Keeps only unique elements within the Vector, removing any duplicates.
|
||||
@ -1102,18 +1103,16 @@ type Empty_Error
|
||||
to_display_text : Text
|
||||
to_display_text self = "The vector is empty."
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
An error indicating that the vector contains incomparable types.
|
||||
|
||||
type Incomparable_Values_Error
|
||||
|
||||
## ADVANCED
|
||||
Catches possible errors from comparing values and throws an
|
||||
Incomparable_Values_Error if any occur.
|
||||
handle_incomparable_value ~function =
|
||||
handle t = Panic.catch t handler=(_-> Error.throw Incomparable_Values_Error)
|
||||
handle No_Such_Method_Error_Data <| handle Type_Error_Data <| handle Unsupported_Argument_Types_Data <| function
|
||||
handle ClassCastException <|
|
||||
handle No_Such_Method_Error_Data <|
|
||||
handle Type_Error_Data <|
|
||||
handle Unsupported_Argument_Types_Data <|
|
||||
function
|
||||
|
||||
## PRIVATE
|
||||
Creates a new vector where for each range, a corresponding section of the
|
||||
|
@ -771,3 +771,9 @@ type Unsupported_File_Type
|
||||
to_display_text : Text
|
||||
to_display_text self =
|
||||
"The "+self.filename+" has a type that is not supported."
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
An error indicating that some elements are incomparable.
|
||||
|
||||
type Incomparable_Values_Error
|
||||
|
@ -529,7 +529,7 @@ type Table
|
||||
descending order.
|
||||
|
||||
table.order_by (Sort_Column_Selector.By_Index [1, Sort_Column.Index -7 Sort_Direction.Descending])
|
||||
order_by : Sort_Column_Selector -> Text_Ordering -> Problem_Behavior -> Table
|
||||
order_by : Sort_Column_Selector -> Text_Ordering -> Problem_Behavior -> Table ! Incomparable_Values_Error
|
||||
order_by self (columns = (Sort_Column_Selector.By_Name [(Sort_Column.Name (self.columns.at 0 . name))])) text_ordering=Text_Ordering.Default on_problems=Report_Warning = Panic.handle_wrapped_dataflow_error <|
|
||||
problem_builder = Problem_Builder.new
|
||||
columns_for_ordering = Table_Helpers.prepare_order_by self.columns columns problem_builder
|
||||
|
@ -650,7 +650,7 @@ type Table
|
||||
table = Examples.inventory_table
|
||||
table.order_by (Sort_Column_Selector.By_Name ["total_stock", Sort_Column.Name "sold_stock" Sort_Direction.Descending])
|
||||
|
||||
order_by : Sort_Column_Selector -> Text_Ordering -> Problem_Behavior -> Table
|
||||
order_by : Sort_Column_Selector -> Text_Ordering -> Problem_Behavior -> Table ! Incomparable_Values_Error
|
||||
order_by self (columns = (Sort_Column_Selector.By_Name [(Sort_Column.Name (self.columns.at 0 . name))])) text_ordering=Text_Ordering.Default on_problems=Report_Warning =
|
||||
problem_builder = Problem_Builder.new
|
||||
columns_for_ordering = Table_Helpers.prepare_order_by self.columns columns problem_builder
|
||||
@ -658,7 +658,7 @@ type Table
|
||||
selected_columns = columns_for_ordering.map c->c.column.java_column
|
||||
ordering = columns_for_ordering.map c->c.associated_selector.direction.to_sign
|
||||
comparator = Comparator.for_text_ordering text_ordering
|
||||
java_table = Illegal_Argument_Error.handle_java_exception <|
|
||||
java_table = Illegal_Argument_Error.handle_java_exception <| Vector.handle_incomparable_value <|
|
||||
self.java_table.orderBy selected_columns.to_array ordering.to_array comparator
|
||||
Table_Data java_table
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
package org.enso.base;
|
||||
|
||||
import org.graalvm.polyglot.Value;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ObjectComparator implements Comparator<Object> {
|
||||
private static ObjectComparator INSTANCE;
|
||||
@ -17,7 +20,7 @@ public class ObjectComparator implements Comparator<Object> {
|
||||
* passed to allow calling back from Java.
|
||||
* @return Comparator object.
|
||||
*/
|
||||
public static ObjectComparator getInstance(BiFunction<Object, Object, Long> fallbackComparator) {
|
||||
public static ObjectComparator getInstance(Function<Object, Function<Object, Value>> fallbackComparator) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new ObjectComparator(fallbackComparator);
|
||||
}
|
||||
@ -25,22 +28,22 @@ public class ObjectComparator implements Comparator<Object> {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final BiFunction<Object, Object, Long> fallbackComparator;
|
||||
private final BiFunction<String, String, Long> textComparator;
|
||||
private final Function<Object, Function<Object, Value>> fallbackComparator;
|
||||
private final Function<String, Function<String, Value>> textComparator;
|
||||
|
||||
|
||||
public ObjectComparator() {
|
||||
this(
|
||||
(a, b) -> {
|
||||
(a) -> (b) -> {
|
||||
throw new ClassCastException("Incomparable keys.");
|
||||
});
|
||||
}
|
||||
|
||||
public ObjectComparator(BiFunction<Object, Object, Long> fallbackComparator) {
|
||||
this(fallbackComparator, (a, b) -> Long.valueOf(Text_Utils.compare_normalized(a, b)));
|
||||
public ObjectComparator(Function<Object, Function<Object, Value>> fallbackComparator) {
|
||||
this(fallbackComparator, (a) -> (b) -> Value.asValue(Text_Utils.compare_normalized(a, b)));
|
||||
}
|
||||
|
||||
private ObjectComparator(BiFunction<Object, Object, Long> fallbackComparator, BiFunction<String, String, Long> textComparator) {
|
||||
private ObjectComparator(Function<Object, Function<Object, Value>> fallbackComparator, Function<String, Function<String, Value>> textComparator) {
|
||||
this.fallbackComparator = fallbackComparator;
|
||||
this.textComparator = textComparator;
|
||||
}
|
||||
@ -51,7 +54,7 @@ public class ObjectComparator implements Comparator<Object> {
|
||||
* @return Comparator object.
|
||||
*/
|
||||
public ObjectComparator withCaseInsensitivity(Locale locale) {
|
||||
return new ObjectComparator(this.fallbackComparator, (a, b) -> Long.valueOf(Text_Utils.compare_normalized_ignoring_case(a, b, locale)));
|
||||
return new ObjectComparator(this.fallbackComparator, (a) -> (b) -> Value.asValue(Text_Utils.compare_normalized_ignoring_case(a, b, locale)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,7 +62,7 @@ public class ObjectComparator implements Comparator<Object> {
|
||||
* @param textComparator custom comparator for Text.
|
||||
* @return Comparator object.
|
||||
*/
|
||||
public ObjectComparator withCustomTextComparator(BiFunction<String, String, Long> textComparator) {
|
||||
public ObjectComparator withCustomTextComparator(Function<String, Function<String, Value>> textComparator) {
|
||||
return new ObjectComparator(this.fallbackComparator, textComparator);
|
||||
}
|
||||
|
||||
@ -118,7 +121,7 @@ public class ObjectComparator implements Comparator<Object> {
|
||||
|
||||
// Text
|
||||
if (thisValue instanceof String thisString && thatValue instanceof String thatString) {
|
||||
return textComparator.apply(thisString, thatString).intValue();
|
||||
return convertComparatorResult(textComparator.apply(thisString).apply(thatString));
|
||||
}
|
||||
|
||||
// DateTimes
|
||||
@ -126,15 +129,10 @@ public class ObjectComparator implements Comparator<Object> {
|
||||
if (thatValue instanceof LocalDate thatDate) {
|
||||
return thisDate.compareTo(thatDate);
|
||||
}
|
||||
if (thatValue instanceof LocalDateTime thatDateTime) {
|
||||
return thisDate.atStartOfDay().compareTo(thatDateTime);
|
||||
}
|
||||
}
|
||||
if (thisValue instanceof LocalDateTime thisDateTime) {
|
||||
if (thatValue instanceof LocalDate thatDate) {
|
||||
return thisDateTime.compareTo(thatDate.atStartOfDay());
|
||||
}
|
||||
if (thatValue instanceof LocalDateTime thatDateTime) {
|
||||
|
||||
if (thisValue instanceof ZonedDateTime thisDateTime) {
|
||||
if (thatValue instanceof ZonedDateTime thatDateTime) {
|
||||
return thisDateTime.compareTo(thatDateTime);
|
||||
}
|
||||
}
|
||||
@ -147,6 +145,14 @@ public class ObjectComparator implements Comparator<Object> {
|
||||
}
|
||||
|
||||
// Fallback to Enso
|
||||
return fallbackComparator.apply(thisValue, thatValue).intValue();
|
||||
return convertComparatorResult(fallbackComparator.apply(thisValue).apply(thatValue));
|
||||
}
|
||||
|
||||
private static int convertComparatorResult(Value comparatorResult) {
|
||||
if (comparatorResult.isNumber() && comparatorResult.fitsInInt()) {
|
||||
return comparatorResult.asInt();
|
||||
} else {
|
||||
throw new ClassCastException("Comparator returned a non-integer value: " + comparatorResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
40
test/Benchmarks/src/Table/Sorting.enso
Normal file
40
test/Benchmarks/src/Table/Sorting.enso
Normal file
@ -0,0 +1,40 @@
|
||||
from Standard.Base import all
|
||||
from Standard.Base.Data.Index_Sub_Range import Sample
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
import Standard.Table
|
||||
from Standard.Table import Sort_Column_Selector
|
||||
from Standard.Table.Data.Aggregate_Column import all
|
||||
|
||||
import Standard.Test.Bench
|
||||
|
||||
type My
|
||||
Data x
|
||||
|
||||
compare_to self other =
|
||||
other.x . compare_to self.x
|
||||
|
||||
vector_size = 100000
|
||||
iter_size = 20
|
||||
num_iterations = 10
|
||||
|
||||
# The Benchmarks ==============================================================
|
||||
main =
|
||||
ints = (0.up_to vector_size).to_vector.take (Sample vector_size 42)
|
||||
start = Date_Time.new 1990 1 1
|
||||
dates = ints.map x->
|
||||
start + x.seconds
|
||||
objects = ints.map My.Data
|
||||
|
||||
ints_table = Table.new [['ints', ints]]
|
||||
dates_table = Table.new [['dates', dates]]
|
||||
objects_table = Table.new [['objects', objects]]
|
||||
|
||||
Bench.measure (ints_table.order_by (Sort_Column_Selector.By_Index [0])) "Table.order_by ints" iter_size num_iterations
|
||||
Bench.measure (ints.sort) "Vector.sort ints" iter_size num_iterations
|
||||
|
||||
Bench.measure (dates_table.order_by (Sort_Column_Selector.By_Index [0])) "Table.order_by dates" iter_size num_iterations
|
||||
Bench.measure (dates.sort) "Vector.sort dates" iter_size num_iterations
|
||||
|
||||
Bench.measure (objects_table.order_by (Sort_Column_Selector.By_Index [0])) "Table.order_by objects" iter_size num_iterations
|
||||
Bench.measure (objects.sort) "Vector.sort objects" iter_size num_iterations
|
@ -1,4 +1,5 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Runtime.Ref
|
||||
|
||||
import Standard.Test.Bench
|
||||
|
||||
@ -15,16 +16,16 @@ vector_size = 1000000
|
||||
iter_size = 100
|
||||
num_iterations = 10
|
||||
|
||||
make_sorted_ascending_vec : Integer -> Base.Vector.Vector
|
||||
make_sorted_ascending_vec : Integer -> Vector.Vector
|
||||
make_sorted_ascending_vec n = 0.up_to n+1 . to_vector
|
||||
|
||||
make_partially_sorted_vec : Integer -> Base.Vector.Vector
|
||||
make_partially_sorted_vec : Integer -> Vector.Vector
|
||||
make_partially_sorted_vec n =
|
||||
random_gen = Random.new n
|
||||
direction = Ref.new Sort_Direction.Ascending
|
||||
last_num = Ref.new 0
|
||||
run_length = Ref.new 0
|
||||
Base.Vector.fill n <|
|
||||
Vector.fill n <|
|
||||
case (run_length.get) == 0 of
|
||||
True ->
|
||||
new_direction = if random_gen.nextDouble > 0 then Sort_Direction.Ascending else
|
||||
|
@ -102,18 +102,20 @@ spec =
|
||||
times = ["times", [Time_Of_Day.new 18 00, Time_Of_Day.new 1 2 34, Nothing, Time_Of_Day.new]]
|
||||
datetimes = ["datetimes", [Date_Time.new 2000, Date_Time.new 1999 1 2 3 4 5, Nothing, Date_Time.new 2022 8 27 11 22 25]]
|
||||
mixed = ["mixed", [1, "a", Nothing, Date.new 2022 8 27]]
|
||||
mixed_dates = ["mixed_dates", [Date.new 1999 1 2, Date_Time.new 1999 1 2 3 40, Date.new 1999 1 2, Date_Time.new 1999 1 2 3 40]]
|
||||
just_nulls = ["just_nulls", [Nothing, Nothing, Nothing, Nothing]]
|
||||
|
||||
table = Table.new [strs, ints, doubles, doubles_and_ints, custom_objects, dates, times, datetimes, mixed, just_nulls]
|
||||
table = Table.new [strs, ints, doubles, doubles_and_ints, custom_objects, dates, times, datetimes, mixed, mixed_dates, just_nulls]
|
||||
table.at "strs" . storage_type . should_equal Storage.Text
|
||||
table.at "ints" . storage_type . should_equal Storage.Integer
|
||||
table.at "doubles" . storage_type . should_equal Storage.Decimal
|
||||
table.at "doubles_and_ints" . storage_type . should_equal Storage.Decimal
|
||||
table.at "custom_objects" . storage_type . should_equal Storage.Any
|
||||
table.at "mixed" . storage_type . should_equal Storage.Any
|
||||
table.at "dates" . storage_type . should_equal Storage.Date
|
||||
table.at "times" . storage_type . should_equal Storage.Time_Of_Day
|
||||
table.at "datetimes" . storage_type . should_equal Storage.Date_Time
|
||||
table.at "mixed" . storage_type . should_equal Storage.Any
|
||||
table.at "mixed_dates" . storage_type . should_equal Storage.Any
|
||||
table.at "just_nulls" . storage_type . should_equal Storage.Any
|
||||
|
||||
pending_python_missing = if Polyglot.is_language_installed "python" . not then
|
||||
@ -478,9 +480,13 @@ spec =
|
||||
reals = [1.3, 4.6, 3.2, 5.2, 1.6]
|
||||
bools = [False, False, True, True, False]
|
||||
texts = ["foo", "foo", "bar", "baz", "spam"]
|
||||
objs = [Cons 1 2, Cons 2 3, Cons 6 7, Cons 8 9, Cons 10 30]
|
||||
dates = [Date.new 2020, Date.new 1999, Date.new 2000 10 3, Date.new 1999 12 31, Date.new 2000 2 7]
|
||||
times = [Time_Of_Day.new 12, Time_Of_Day.new 1 30 40, Time_Of_Day.new 23 59 59, Time_Of_Day.new 12 30 0, Time_Of_Day.new 10 20 30]
|
||||
datetimes = [Date_Time.new 2020 1 1 12, Date_Time.new 1999 1 1 1 30 40, Date_Time.new 2000 10 3 23 59 59, Date_Time.new 1999 12 31 12 30 0, Date_Time.new 2000 10 3 10 20 30]
|
||||
objs = [My_Data 100 2, My_Data 2 3, My_Data 6 7, My_Data 8 9, My_Data 10 30]
|
||||
mixed_dates = [Date.new 1999 1 2, Date_Time.new 1999 1 2 3 40, Date.new 1999 1 2, Date_Time.new 1999 1 2 3 40, Date.new 2000]
|
||||
|
||||
df = Table.new [['ord', ord], ['ints', ints], ['reals', reals], ['bools', bools], ['texts', texts], ['objs', objs]]
|
||||
df = Table.new [['ord', ord], ['ints', ints], ['reals', reals], ['bools', bools], ['texts', texts], ['objs', objs], ['dates', dates], ['times', times], ['datetimes', datetimes], ['mixed_dates', mixed_dates]]
|
||||
r = df.order_by (Sort_Column_Selector.By_Name ['ord'])
|
||||
|
||||
r.at 'ints' . to_vector . should_equal [1, 5, 3, 2, 4]
|
||||
@ -495,9 +501,38 @@ spec =
|
||||
r.at 'texts' . to_vector . should_equal ['foo', 'spam', 'bar', 'foo', 'baz']
|
||||
df.at 'texts' . to_vector . should_equal texts
|
||||
|
||||
r.at 'objs' . to_vector . should_equal [Cons 1 2, Cons 10 30, Cons 6 7, Cons 2 3, Cons 8 9]
|
||||
r.at 'objs' . to_vector . should_equal [My_Data 100 2, My_Data 10 30, My_Data 6 7, My_Data 2 3, My_Data 8 9]
|
||||
df.at 'objs' . to_vector . should_equal objs
|
||||
|
||||
r.at 'dates' . to_vector . should_equal [Date.new 2020, Date.new 2000 2 7, Date.new 2000 10 3, Date.new 1999, Date.new 1999 12 31]
|
||||
df.at 'dates' . to_vector . should_equal dates
|
||||
|
||||
r.at 'times' . to_vector . should_equal [Time_Of_Day.new 12, Time_Of_Day.new 10 20 30, Time_Of_Day.new 23 59 59, Time_Of_Day.new 1 30 40, Time_Of_Day.new 12 30 0]
|
||||
df.at 'times' . to_vector . should_equal times
|
||||
|
||||
r.at 'datetimes' . to_vector . should_equal [Date_Time.new 2020 1 1 12, Date_Time.new 2000 10 3 10 20 30, Date_Time.new 2000 10 3 23 59 59, Date_Time.new 1999 1 1 1 30 40, Date_Time.new 1999 12 31 12 30 0]
|
||||
df.at 'datetimes' . to_vector . should_equal datetimes
|
||||
|
||||
# TODO move this test to Common_Table_Spec once we support dates there
|
||||
r2 = df.order_by (Sort_Column_Selector.By_Name ['dates'])
|
||||
r2.at 'dates' . to_vector . should_equal [Date.new 1999, Date.new 1999 12 31, Date.new 2000 2 7, Date.new 2000 10 3, Date.new 2020]
|
||||
r2.at 'ints' . to_vector . should_equal [2, 4, 5, 3, 1]
|
||||
|
||||
r3 = df.order_by (Sort_Column_Selector.By_Name ['times'])
|
||||
r3.at 'times' . to_vector . should_equal [Time_Of_Day.new 1 30 40, Time_Of_Day.new 10 20 30, Time_Of_Day.new 12, Time_Of_Day.new 12 30 0, Time_Of_Day.new 23 59 59]
|
||||
r3.at 'ints' . to_vector . should_equal [2, 5, 1, 4, 3]
|
||||
|
||||
r4 = df.order_by (Sort_Column_Selector.By_Name ['datetimes'])
|
||||
r4.at 'datetimes' . to_vector . should_equal [Date_Time.new 1999 1 1 1 30 40, Date_Time.new 1999 12 31 12 30 0, Date_Time.new 2000 10 3 10 20 30, Date_Time.new 2000 10 3 23 59 59, Date_Time.new 2020 1 1 12]
|
||||
r4.at 'ints' . to_vector . should_equal [2, 4, 5, 3, 1]
|
||||
|
||||
r5 = df.order_by (Sort_Column_Selector.By_Name ['objs'])
|
||||
r5.at 'objs' . to_vector . should_equal [My_Data 2 3, My_Data 6 7, My_Data 8 9, My_Data 10 30, My_Data 100 2]
|
||||
r5.at 'ints' . to_vector . should_equal [2, 3, 4, 5, 1]
|
||||
|
||||
r6 = df.order_by (Sort_Column_Selector.By_Name ['mixed_dates'])
|
||||
r6 . should_fail_with Incomparable_Values_Error
|
||||
|
||||
Test.group "Sorting Columns" <|
|
||||
Test.specify 'should sort columns with specified ordering and missing placement' <|
|
||||
c = Column.from_vector 'foo' [1, 7, Nothing, 4, 8, Nothing]
|
||||
|
@ -20,7 +20,7 @@ type No_Ord
|
||||
# Tests
|
||||
|
||||
spec = Test.group "Object Comparator" <|
|
||||
handle_classcast = Panic.catch ClassCastException handler=(_ -> Error.throw Vector.Incomparable_Values_Error)
|
||||
handle_classcast = Panic.catch ClassCastException handler=(_ -> Error.throw Incomparable_Values_Error)
|
||||
default_comparator a b = handle_classcast <| Comparator.new.compare a b
|
||||
case_insensitive a b = handle_classcast <| Comparator.for_text_ordering Text_Ordering.Case_Insensitive . compare a b
|
||||
|
||||
@ -58,7 +58,7 @@ spec = Test.group "Object Comparator" <|
|
||||
((default_comparator (Ord.Value 1) (Ord.Value 1)) == 0) . should_equal True
|
||||
|
||||
Test.specify "should fail gracefully for incomparable items" <|
|
||||
(default_comparator 1 True).should_fail_with Vector.Incomparable_Values_Error
|
||||
(default_comparator (No_Ord.Value 1) (No_Ord.Value 2)).should_fail_with Vector.Incomparable_Values_Error
|
||||
(default_comparator 1 True).should_fail_with Incomparable_Values_Error
|
||||
(default_comparator (No_Ord.Value 1) (No_Ord.Value 2)).should_fail_with Incomparable_Values_Error
|
||||
|
||||
main = Test.Suite.run_main spec
|
||||
|
@ -150,11 +150,11 @@ spec =
|
||||
|
||||
Test.specify "should fail with Incomparable_Values_Error on custom type without compare_to" <|
|
||||
no_ord_set.compute . should_equal 3
|
||||
no_ord_set.compute Minimum . should_fail_with Vector.Incomparable_Values_Error
|
||||
no_ord_set.compute Maximum . should_fail_with Vector.Incomparable_Values_Error
|
||||
no_ord_set.compute Minimum . should_fail_with Incomparable_Values_Error
|
||||
no_ord_set.compute Maximum . should_fail_with Incomparable_Values_Error
|
||||
|
||||
Test.specify "should fail with Incomparable_Values_Error on mixed Vectors" <|
|
||||
[1, False].compute Minimum . should_fail_with Vector.Incomparable_Values_Error
|
||||
[1, False].compute Minimum . should_fail_with Incomparable_Values_Error
|
||||
|
||||
Test.group "Rank Data" <|
|
||||
Test.specify "can rank a Decimal data series" <|
|
||||
@ -179,10 +179,10 @@ spec =
|
||||
|
||||
Test.specify "should fail with Incomparable_Values_Error on custom type without compare_to" <|
|
||||
values = [No_Ord.Value 10, No_Ord.Value 2, No_Ord.Value 9]
|
||||
rank_data values . should_fail_with Vector.Incomparable_Values_Error
|
||||
rank_data values . should_fail_with Incomparable_Values_Error
|
||||
|
||||
Test.specify "should fail with Incomparable_Values_Error on mixed Vectors" <|
|
||||
rank_data [1, "A"] . should_fail_with Vector.Incomparable_Values_Error
|
||||
rank_data [1, "A"] . should_fail_with Incomparable_Values_Error
|
||||
|
||||
Test.specify "should fail with Illegal_Argument_Error on Vectors with Nothing" <|
|
||||
rank_data [1, Nothing, 4] . should_fail_with Illegal_Argument_Error_Data
|
||||
|
@ -133,6 +133,12 @@ spec_with name create_new_date parse_date =
|
||||
date_1>date_2 . should_be_true
|
||||
date_1<date_2 . should_be_false
|
||||
|
||||
datetime = Date_Time.new 2021 1 2 12 40
|
||||
date_1.compare_to datetime . should_fail_with Type_Error_Data
|
||||
date_1<datetime . should_fail_with Type_Error_Data
|
||||
date_1>=datetime . should_fail_with Type_Error_Data
|
||||
date_1==datetime . should_be_false
|
||||
|
||||
Test.specify "should correctly determine the type of date" <|
|
||||
new_date = create_new_date 2020 6 1
|
||||
parsed_date = parse_date "2021-01-02"
|
||||
|
@ -315,6 +315,15 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
time_1>time_2 . should_be_true
|
||||
time_1<time_2 . should_be_false
|
||||
|
||||
tod = Time_Of_Day.new 4 0 10
|
||||
time_1.compare_to tod . should_fail_with Type_Error_Data
|
||||
time_1<tod . should_fail_with Type_Error_Data
|
||||
time_1==tod . should_be_false
|
||||
|
||||
date = Date.new 2021 1 1
|
||||
time_1.compare_to date . should_fail_with Type_Error_Data
|
||||
time_1==date . should_be_false
|
||||
|
||||
Test.specify "should correctly determine the type of datetime" <|
|
||||
new_datetime = create_new_datetime 2020 6 1 10 0 0
|
||||
parsed_datetime = parse_datetime "2021-02-01T00:30:12.7102[UTC]"
|
||||
|
@ -117,6 +117,8 @@ specWith name create_new_time parse_time =
|
||||
time_1>time_2 . should_be_true
|
||||
time_1<time_2 . should_be_false
|
||||
|
||||
time_2.compare_to (Date_Time.new 1999 1 1 4 0 10) . should_fail_with Type_Error_Data
|
||||
|
||||
Test.specify "should correctly determine the type of timeofday" <|
|
||||
new_timeofday = create_new_time 15 37 58
|
||||
parsed_timeofday = parse_time "10:00:00"
|
||||
|
@ -387,6 +387,16 @@ spec = Test.group "Vectors" <|
|
||||
empty_vec.sort . should_equal []
|
||||
short_vec.sort . should_equal short_expected
|
||||
|
||||
["aa", "bb", "ba"].sort . should_equal ["aa", "ba", "bb"]
|
||||
[Date.new 2000, Date.new 1999 10 11, Date.new 1990].sort . should_equal [Date.new 1990, Date.new 1999 10 11, Date.new 2000]
|
||||
[Time_Of_Day.new 12, Time_Of_Day.new 10 30].sort . should_equal [Time_Of_Day.new 10 30, Time_Of_Day.new 12]
|
||||
[Date_Time.new 2000 12 30 12 30, Date_Time.new 2000 12 30 12 00].sort . should_equal [Date_Time.new 2000 12 30 12 00, Date_Time.new 2000 12 30 12 30]
|
||||
|
||||
["aa", 2].sort . should_fail_with Incomparable_Values_Error
|
||||
[2, Date.new 1999].sort . should_fail_with Incomparable_Values_Error
|
||||
[Date_Time.new 1999 1 1 12 30, Date.new 1999].sort . should_fail_with Incomparable_Values_Error
|
||||
[Date_Time.new 1999 1 1 12 30, Time_Of_Day.new 12 30].sort . should_fail_with Incomparable_Values_Error
|
||||
|
||||
Test.specify "should leave the original vector unchanged" <|
|
||||
non_empty_vec = [2, 4, 2, 3, 2, 3]
|
||||
sorted = non_empty_vec.sort
|
||||
@ -473,9 +483,9 @@ spec = Test.group "Vectors" <|
|
||||
[].distinct . should_equal []
|
||||
|
||||
Test.specify "should throw a clean error for incomparable types" <|
|
||||
["a", 2].distinct . should_fail_with Vector.Incomparable_Values_Error
|
||||
[2, "a", Integer, "a", 2].distinct . should_fail_with Vector.Incomparable_Values_Error
|
||||
[Pair_Data 1 2, Pair_Data 3 4].distinct . should_fail_with Vector.Incomparable_Values_Error
|
||||
["a", 2].distinct . should_fail_with Incomparable_Values_Error
|
||||
[2, "a", Integer, "a", 2].distinct . should_fail_with Incomparable_Values_Error
|
||||
[Pair_Data 1 2, Pair_Data 3 4].distinct . should_fail_with Incomparable_Values_Error
|
||||
|
||||
Test.specify "should correctly handle distinct with custom types like Atoms that implement compare_to" <|
|
||||
[T.Value 1 2, T.Value 3 3, T.Value 1 2].distinct . should_equal [T.Value 1 2, T.Value 3 3]
|
||||
|
Loading…
Reference in New Issue
Block a user