Преглед изворни кода

Implement "order_by" functions

Billy Barrow пре 3 недеља
родитељ
комит
feb53d207d

+ 1 - 1
src/lib/DataStructures/SortedSeries.vala

@@ -26,7 +26,7 @@ namespace Invercargill.DataStructures {
                 right_child = this;
             }
 
-            public T sample() {
+            public unowned T sample() {
                 return first_value->item;
             }
 

+ 15 - 0
src/lib/Enumerable.vala

@@ -170,6 +170,21 @@ namespace Invercargill {
             return new Sort<T>(this, (owned)compare);
         }
 
+        public virtual Enumerable<T> order_by<TKey>(owned TransformDelegate<T, TKey> key_selector, owned CompareDelegate<TKey>? comparitor = null) {
+            return new Order<T, TKey>(this, (owned)key_selector, (owned)comparitor);
+        }
+
+        public virtual Enumerable<T> order_by_descending<TKey>(owned TransformDelegate<T, TKey> key_selector, owned CompareDelegate<TKey>? comparitor = null) {
+            return new Order<T, TKey>(this, (owned)key_selector, (owned)comparitor, true);
+        }
+
+        public virtual Enumerable<T> order_by_complex(ItemDelegate<OrderConfiguration<T>> config) {
+            var order_config = new OrderConfiguration<T>();
+            config(order_config);
+            var comparitor = order_config.build_comparitor();
+            return sort((owned)comparitor);
+        }
+
         public virtual Enumerable<T> concat(Enumerable<T> other) {
             return new Concat<T>(this, other);
         }

+ 46 - 0
src/lib/Modifiers/Order.vala

@@ -0,0 +1,46 @@
+namespace Invercargill.Modifiers {
+
+    public class Order<T, TKey> : Enumerable<T> {
+        private Enumerable<T> input;
+        private CompareDelegate<TKey> compare_func;
+        private TransformDelegate<T, TKey> key_selector;
+        private bool reversed;
+
+        public Order(Enumerable<T> input, owned TransformDelegate<T, TKey> key_selector, owned CompareDelegate<T>? comparitor = null, bool reversed = false) {
+            this.input = input;
+            this.key_selector = (owned)key_selector;
+            this.reversed = reversed;
+            if(comparitor != null) {
+                compare_func = (owned)comparitor;
+            }
+            else {
+                compare_func = Operators.comparison<TKey>();
+            }
+        }
+
+        public override int? peek_count() {
+            return input.peek_count();
+        }
+
+        public override EnumerableInfo get_info() {
+            return new EnumerableInfo.infer_single(this, EnumerableCategory.COMPUTED, input);
+        }
+
+        private int do_compare(T a, T b) {
+            var key_a = key_selector(a);
+            var key_b = key_selector(b);
+            if(reversed) {
+                return compare_func(key_b, key_a);
+            }
+            else {
+                return compare_func(key_a, key_b);
+            }
+        }
+
+        public override Tracker<T> get_tracker() {
+            var list = new DataStructures.SortedSeries<T>(do_compare);
+            list.add_all(input);
+            return list.get_tracker();
+        }
+    }
+}

+ 1 - 1
src/lib/Operators/Stringify.vala

@@ -45,7 +45,7 @@ namespace Invercargill.Operators {
         else if(type.is_a(typeof(DataStructures.BinaryData))) {
             return i => "0x" + ((DataStructures.BinaryData)i).to_string(b => b.to_string("%x"));
         }
-        return i => @"[$(type.name()) at 0x$(((int)&i).to_string("%x"))]";
+        return i => @"[$(type.name()) at 0x$(((int)i).to_string("%x"))]";
     }
 
 }

+ 80 - 0
src/lib/OrderConfiguration.vala

@@ -0,0 +1,80 @@
+using Invercargill.DataStructures;
+namespace Invercargill {
+
+
+    public class OrderConfiguration<T> {
+        internal Series<GenericComparitor<T>> comparitors = new Series<GenericComparitor<T>>();
+
+        internal void add_comparitor<TKey>(owned TransformDelegate<T, TKey> key_selector, owned CompareDelegate<TKey>? key_comparitor, bool reversed) {
+            var cmp = key_comparitor ?? Operators.comparison<TKey>();
+            comparitors.add(new Comparitor<T, TKey>((owned)key_selector, (owned)cmp, reversed));
+        }
+        
+        public ThenOrderConfiguration<T> order_by<TKey>(owned TransformDelegate<T, TKey> key_selector, owned CompareDelegate<TKey>? key_comparitor = null) {
+            add_comparitor<TKey>((owned)key_selector, (owned)key_comparitor, false);
+            return new ThenOrderConfiguration<T>(this);
+        }
+
+        public ThenOrderConfiguration<T> order_descending<TKey>(owned TransformDelegate<T, TKey> key_selector, owned CompareDelegate<TKey>? key_comparitor = null) {
+            add_comparitor<TKey>((owned)key_selector, (owned)key_comparitor, true);
+            return new ThenOrderConfiguration<T>(this);
+        }
+
+        public CompareDelegate<T> build_comparitor() {
+            var cmps = comparitors.to_buffer();
+            return (a, b) => {
+                var result = 0;
+                foreach (var cmp in cmps) {
+                    result = cmp.compare(a, b);
+                    if(result != 0) {
+                        return result;
+                    }
+                }
+                return result;
+            };
+        }
+
+    }
+
+    public class ThenOrderConfiguration<T> {
+        private OrderConfiguration<T> config;
+
+        internal ThenOrderConfiguration(OrderConfiguration<T> config) {
+            this.config = config;
+        }
+        
+        public ThenOrderConfiguration<T> then_by<TKey>(owned TransformDelegate<T, TKey> key_selector, owned CompareDelegate<TKey>? key_comparitor = null) {
+            config.add_comparitor<TKey>((owned)key_selector, (owned)key_comparitor, false);
+            return this;
+        }
+
+        public ThenOrderConfiguration<T> then_by_descending<TKey>(owned TransformDelegate<T, TKey> key_selector, owned CompareDelegate<TKey>? key_comparitor = null) {
+            config.add_comparitor<TKey>((owned)key_selector, (owned)key_comparitor, true);
+            return this;
+        }
+    }
+
+    private abstract class GenericComparitor<T> {
+        public abstract int compare(T a, T b);
+    }
+
+    private class Comparitor<T, TKey> : GenericComparitor<T> {
+        public bool reversed { get; set; }
+        public TransformDelegate<T, TKey> key_selector { get; set; }
+        public CompareDelegate<TKey> key_comparitor { get; set; }
+
+        public Comparitor(owned TransformDelegate<T, TKey> key_selector, owned CompareDelegate<TKey> key_comparitor, bool reversed) {
+            this.reversed = reversed;
+            this.key_comparitor = (owned)key_comparitor;
+            this.key_selector = (owned)key_selector;
+        }
+
+        public override int compare(T a, T b) {
+            if(reversed) {
+                return key_comparitor(key_selector(b), key_selector(a));
+            }
+            return key_comparitor(key_selector(a), key_selector(b));
+        }
+    }
+
+}

+ 2 - 0
src/lib/meson.build

@@ -28,6 +28,7 @@ sources += files('EnumerableInfo.vala')
 sources += files('StickyPromotion.vala')
 sources += files('Element.vala')
 sources += files('Debug.vala')
+sources += files('OrderConfiguration.vala')
 
 sources += files('Modifiers/Transform.vala')
 sources += files('Modifiers/Filter.vala')
@@ -46,6 +47,7 @@ sources += files('Modifiers/Concat.vala')
 sources += files('Modifiers/Zip.vala')
 sources += files('Modifiers/Sealed.vala')
 sources += files('Modifiers/Interleave.vala')
+sources += files('Modifiers/Order.vala')
 
 sources += files('Wrappers/Array.vala')
 sources += files('Wrappers/GeeIterable.vala')

+ 86 - 0
src/tests/Integration/OrderBy.vala

@@ -0,0 +1,86 @@
+using Invercargill;
+using Invercargill.Convert;
+
+void order_by_tests() {
+
+    Test.add_func("/invercargill/operator/order_by/ascending_int", () => {
+        var items = ClassToOrder.data();
+        var expected_ids = ate(new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
+
+        var result = items.order_by<int>(i => i.id);
+        
+        assert_true(expected_ids.matches(result.select<int>(i => i.id), (a, b) => a == b));
+    });
+
+    Test.add_func("/invercargill/operator/order_by/descending_int", () => {
+        var items = ClassToOrder.data();
+        var expected_ids = ate(new int[] { 8, 7, 6, 5, 4, 3, 2, 1 });
+
+        var result = items.order_by_descending<int>(i => i.id);
+        
+        assert_true(expected_ids.matches(result.select<int>(i => i.id), (a, b) => a == b));
+    });
+
+    Test.add_func("/invercargill/operator/order_by/ascending_string", () => {
+        var items = ClassToOrder.data();
+        var expected_ids = ate(new int[] { 2, 4, 8, 3, 7, 6, 1, 5 });
+
+        var result = items.order_by<string>(i => i.name);
+        
+        assert_true(expected_ids.matches(result.select<int>(i => i.id), (a, b) => a == b));
+    });
+
+    Test.add_func("/invercargill/operator/order_by/descending_string", () => {
+        var items = ClassToOrder.data();
+        var expected_ids = ate(new int[] { 5, 1, 6, 7, 3, 8, 4, 2 });
+
+        var result = items.order_by_descending<string>(i => i.name);
+        
+        assert_true(expected_ids.matches(result.select<int>(i => i.id), (a, b) => a == b));
+    });
+
+    Test.add_func("/invercargill/operator/order_by/complex_priority_birthyear_id", () => {
+        var items = ClassToOrder.data();
+        var expected_ids = ate(new int[] { 5, 7, 3, 2, 1, 4, 6, 8 });
+
+        var result = items.order_by_complex(c => c
+            .order_by<int>(i => i.priority)
+            .then_by_descending<int>(i => i.birth_year)
+            .then_by<int>(i => i.id)
+        );
+        result.debug_dump("ordered", i => @"$(i.id): $(i.name)");
+        
+        assert_true(expected_ids.matches(result.select<int>(i => i.id), (a, b) => a == b));
+    });
+}
+
+class ClassToOrder {
+    public string name { get; set; }
+    public int birth_year { get; set; }
+    public int priority { get; set; }
+    public int id { get; set; }
+
+    public ClassToOrder(string name, int birth_year, int priority, int id) {
+        this.name = name;
+        this.birth_year = birth_year;
+        this.priority = priority;
+        this.id = id;
+    }
+
+    public static Enumerable<ClassToOrder> data() {
+        var series = new DataStructures.Series<ClassToOrder>();
+        series.add(new ClassToOrder("Bob", 1997, 4, 4));
+        series.add(new ClassToOrder("Alice", 1997, 2, 2));
+        series.add(new ClassToOrder("James", 1999, 4, 1));
+        series.add(new ClassToOrder("Ella", 1999, 2, 3));
+        series.add(new ClassToOrder("Carter", 2000, 7, 8));
+        series.add(new ClassToOrder("Grace", 2000, 7, 6));
+        series.add(new ClassToOrder("Luca", 2000, 1, 5));
+        series.add(new ClassToOrder("Eve", 2000, 2, 7));
+        return series;
+    }
+
+    ~ClassToOrder() {
+        print(@"Bye $(name)\n");
+    }
+}

+ 1 - 0
src/tests/TestRunner.vala

@@ -22,6 +22,7 @@ public static int main(string[] args) {
     cache_tests();
     sorted_vector_tests();
     sorted_series_tests();
+    order_by_tests();
 
     Test.run();
     

+ 1 - 0
src/tests/meson.build

@@ -21,6 +21,7 @@ sources += files('Integration/PropertyMapper.vala')
 sources += files('Integration/Cache.vala')
 sources += files('Integration/SortedVector.vala')
 sources += files('Integration/SortedSeries.vala')
+sources += files('Integration/OrderBy.vala')
 
 sources += files('Speed/SpeedTest.vala')
 sources += files('Speed/Series.vala')