Kenneth Jørgensen avatar Kenneth Jørgensen committed 07bf415

Implemented SortedMap

Comments (0)

Files changed (5)

 * *Feature:* Implemented `contains()`, `remove()` on `HasManyRelation`.
 * *Feature:* Relations issue change events which a proxied through the model as well.
 * *Feature:* `Loader` implementation to help loader many and multiple levels of models. ([pull/1][pull/1])
+* *Feature:* Added `SortedMap`.
 * *Fix:* toJSON ignored all defined relations.
 * *Fix:* `HasManyRelation` will no longer lose existing `Model` instances when set with a list of IDs.
 * *Fix:* Old value was not supplied on `Model` change events.
 	"src/Collection.coffee"
 	"src/Set.coffee"
 	"src/Map.coffee"
+	"src/SortedMap.coffee"
 	"src/Persistor.coffee"
 	"src/Relation.coffee"
 	"src/Relation/HasOneRelation.coffee"
 /*! Discrete 0.1.0-dev.4 - MIT license */
 (function() {
-  var Async, Calamity, Collection, Discrete, HasManyRelation, HasOneRelation, Loader, Map, Model, ModelRepo, Persistor, Relation, RepoPersistor, Set, calamity, exports, object_toString, root, _, _ref, _ref1,
+  var Async, Calamity, Collection, Discrete, HasManyRelation, HasOneRelation, Loader, Map, Model, ModelRepo, Persistor, Relation, RepoPersistor, Set, SortedMap, calamity, exports, object_toString, root, _, _ref, _ref1,
     __hasProp = {}.hasOwnProperty,
     __slice = [].slice,
     __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
 
   })();
 
+  Discrete.SortedMap = SortedMap = (function(_super) {
+    __extends(SortedMap, _super);
+
+    function SortedMap(values, sorter) {
+      if (_.isFunction(values)) {
+        sorter = values;
+        values = null;
+      }
+      if (!_.isFunction(sorter)) {
+        throw new Error("Sorter is required and must be a function, " + (typeof sorter) + " supplied");
+      }
+      this._sorter = sorter;
+      SortedMap.__super__.constructor.call(this, values);
+      this._sort();
+    }
+
+    SortedMap.prototype.put = function() {
+      var r;
+      r = SortedMap.__super__.put.apply(this, arguments);
+      this._sort();
+      return r;
+    };
+
+    SortedMap.prototype.remove = function() {
+      var r;
+      r = SortedMap.__super__.remove.apply(this, arguments);
+      this._sort();
+      return r;
+    };
+
+    SortedMap.prototype.firstKey = function() {
+      if (this.size() === 0) {
+        return null;
+      }
+      return this._items[0][0];
+    };
+
+    SortedMap.prototype.lastKey = function() {
+      var s;
+      s = this.size();
+      if (s === 0) {
+        return null;
+      }
+      return this._items[s - 1][0];
+    };
+
+    SortedMap.prototype._sort = function() {
+      var _this = this;
+      if (!(this.size() > 1)) {
+        return;
+      }
+      this._items.sort(function(a, b) {
+        return _this._sorter({
+          key: a[0],
+          value: a[1]
+        }, {
+          key: b[0],
+          value: b[1]
+        });
+      });
+      return void 0;
+    };
+
+    return SortedMap;
+
+  })(Map);
+
   Discrete.Persistor = Persistor = (function() {
     function Persistor() {}
 

spec/SortedMapSpec.coffee

+_ = require "underscore"
+sinon = require "sinon"
+{SortedMap, Map, Model} = require "../discrete"
+
+describe "SortedMap", ->
+	map = null
+	sorter = null
+	key1 = name:"Alice",   age:42
+	key2 = name:"Bob",     age:43
+	key3 = name:"Charlie", age:44
+	val1 = floor:1, salary:1000
+	val2 = floor:2, salary:2000
+	val3 = floor:3, salary:3000
+
+	beforeEach ->
+		sorter = sinon.spy (a, b) ->
+			return a.key.age - b.key.age
+		map = new SortedMap sorter
+
+	it "should be empty when first created", ->
+		expect(map.size()).toBe 0
+
+	it "should use sorter to evaluate order", ->
+		expect(sorter.callCount).toBe 0
+		map.put key1, val1
+		map.put key2, val2
+		expect(sorter.callCount).toBe 1
+		expect(sorter.args[0][0].key).toBe key1
+		expect(sorter.args[0][0].value).toBe val1
+		expect(sorter.args[0][1].key).toBe key2
+		expect(sorter.args[0][1].value).toBe val2
+
+	it "should return the first and the last element", ->
+		# Empty.
+		expect(map.firstKey()).toBe null
+		expect(map.lastKey()).toBe null
+		# Add three entries.
+		map.put key1, val1
+		expect(map.firstKey()).toBe key1
+		expect(map.lastKey()).toBe key1
+		map.put key2, val2
+		expect(map.firstKey()).toBe key1
+		expect(map.lastKey()).toBe key2
+		map.put key3, val3
+		expect(map.firstKey()).toBe key1
+		expect(map.lastKey()).toBe key3
+		# Remove two entries.
+		map.remove key3
+		expect(map.firstKey()).toBe key1
+		expect(map.lastKey()).toBe key2
+		map.remove key1
+		expect(map.firstKey()).toBe key2
+		expect(map.lastKey()).toBe key2
+
+	it "should sort initially", ->
+		oldMap = new Map
+		oldMap.put key2, val2
+		oldMap.put key3, val3
+		oldMap.put key1, val1
+		map = new SortedMap oldMap, sorter
+		expect(map.firstKey()).toBe key1
+		expect(map.lastKey()).toBe key3

src/SortedMap.coffee

+# A `SortedMap` works just like `Map`, except elements are continously sorted according to supplied sorter.
+# Loosely based on http://docs.oracle.com/javase/7/docs/api/java/util/SortedMap.html
+Discrete.SortedMap = class SortedMap extends Map
+	constructor: (values, sorter) ->
+		if _.isFunction values
+			sorter = values
+			values = null
+		unless _.isFunction sorter
+			throw new Error "Sorter is required and must be a function, #{typeof sorter} supplied"
+		@_sorter = sorter
+		super values
+		@_sort()
+
+	put: ->
+		r = super
+		@_sort()
+		return r
+
+	remove: ->
+		r = super
+		@_sort()
+		return r
+
+	# Returns the first (lowest) key.
+	firstKey: ->
+		return null if @size() is 0
+		return @_items[0][0]
+
+	# Returns the last (highest) key.
+	lastKey: ->
+		s = @size()
+		return null if s is 0
+		return @_items[s - 1][0]
+
+	# Internal method for sorting the internal array.
+	_sort: ->
+		return unless @size() > 1
+		@_items.sort (a, b) =>
+			return @_sorter (key:a[0],value:a[1]), (key:b[0],value:b[1])
+		return undefined
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.