From 4b5ce620c5db79d2eda3850a90b07197797520fc Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 13 Sep 2013 14:18:44 +0100 Subject: [PATCH 1/5] Added Queue problem and empty setup/solution. --- menu.json | 3 +- problems/queue/problem.txt | 62 ++++++++++++++++++++++++++++++++++++++ problems/queue/setup.js | 0 problems/queue/solution.js | 0 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 problems/queue/problem.txt create mode 100644 problems/queue/setup.js create mode 100644 problems/queue/solution.js diff --git a/menu.json b/menu.json index 3ef0e6d..e258aa8 100644 --- a/menu.json +++ b/menu.json @@ -11,4 +11,5 @@ , "Short Scrabble Words" , "Sublevel" , "Multilevel" -] \ No newline at end of file + , "Queue" +] diff --git a/problems/queue/problem.txt b/problems/queue/problem.txt new file mode 100644 index 0000000..ac92232 --- /dev/null +++ b/problems/queue/problem.txt @@ -0,0 +1,62 @@ +Write a module (not a program) that provides a FIFO queue on top +of a `{bold}level{/bold}` database. Your module should expose a function +that build a named queue and accept a `{bold}levelup{/bold}` instance. +This function should return an object that has the following methods: + * `push`: add an element to the end of the queue. + * `shift`: shifts an element from the front of the queue. + * `clear`: remove all value from the queue. + +`{bold}push{\bold}` accept two arguments, the value to push and +optionally a callback to know when it is finished. + +`{bold}shift{\bold}` accept one argument, an optional callback to +retrieve the value at the front of the queue. + +`{bold}clear{\bold}` accept one argument, an optional callback to +to know when it is finished. + +Remember, all callbacks follow the style of node.js: + + function cb(err, value1, value2, value3, ...) { + if (err) { + // do something + return; + } + } + +---------------------------------------------------------------------- +HINTS: + +Use this boilerplate to get started with your module file: + + function Queue(db, name) { + if (!(this instanceof Queue)) return new Queue(db, name) + + // ... + } + + module.exports = Queue + + Queue.prototype.push = function(element, callback) { + // ... + } + + Queue.prototype.shift = function(callback) { + // ... + // at some point call callback(null, element) + } + + Queue.prototype.clear = function(callback) { + // ... + } + +Before coding, think how the queue data structure can be implemented +on top of LevelUp. In particular, you want to "translate" a `push` into a +level `put`, and `shift` into a limited ranged query. + +All pushed element must be stored in separate keys. You might want to use +the current Date in the standard ISO date format (i.e. the format +generated by the Date object's `toISOString()` method.) as it provides +identifiers sorted in lexicographic order. + +You can also use sublevel to handle the naming of the queues! diff --git a/problems/queue/setup.js b/problems/queue/setup.js new file mode 100644 index 0000000..e69de29 diff --git a/problems/queue/solution.js b/problems/queue/solution.js new file mode 100644 index 0000000..e69de29 From 4a9481e2f160001822c808c1c667d29aeb1700c0 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 17 Sep 2013 09:17:47 +0200 Subject: [PATCH 2/5] Added a basic solution based on level-sublevel. --- problems/queue/setup.js | 7 ++++++ problems/queue/solution.js | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/problems/queue/setup.js b/problems/queue/setup.js index e69de29..ddeb2fa 100644 --- a/problems/queue/setup.js +++ b/problems/queue/setup.js @@ -0,0 +1,7 @@ + +function setup(run, callback) { + console.log(arguments) +} + +module.exports = setup +module.exports.async = true diff --git a/problems/queue/solution.js b/problems/queue/solution.js index e69de29..b85741e 100644 --- a/problems/queue/solution.js +++ b/problems/queue/solution.js @@ -0,0 +1,45 @@ +var sublevel = require('level-sublevel') + +function Queue(db, name) { + if (!(this instanceof Queue)) return new Queue(db, name) + + this._db = sublevel(db).sublevel(name) +} + +module.exports = Queue + +Queue.prototype.push = function(element, callback) { + var key = (new Date()).toISOString() + + this._db.put(key, element, callback) + + return this; +} + +Queue.prototype.shift = function(callback) { + var stream = this._db.createReadStream({ + limit: 1 + }) + + , db = this._db + + stream.once('data', function(data) { + db.del(data.key, function(err) { + callback(err, data.value) + }) + }) + + stream.once('error', callback) + + return this; +} + +Queue.prototype.clear = function(callback) { + this._db.createReadStream() + .pipe(this._db.createWriteStream({ + type: 'del' + })) + .on('close', callback) + + return this; +} From 681e97b4b6c28fd4535f04c5cf7cc8c4790bf540 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 17 Sep 2013 11:45:33 +0200 Subject: [PATCH 3/5] Removed direct dependency on sublevel and link to level-queue-type. --- problems/queue/problem.txt | 6 +++--- problems/queue/solution.js | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/problems/queue/problem.txt b/problems/queue/problem.txt index ac92232..e7bcd87 100644 --- a/problems/queue/problem.txt +++ b/problems/queue/problem.txt @@ -1,6 +1,6 @@ Write a module (not a program) that provides a FIFO queue on top of a `{bold}level{/bold}` database. Your module should expose a function -that build a named queue and accept a `{bold}levelup{/bold}` instance. +that build a queue and accept a `{bold}levelup{/bold}` instance. This function should return an object that has the following methods: * `push`: add an element to the end of the queue. * `shift`: shifts an element from the front of the queue. @@ -29,8 +29,8 @@ HINTS: Use this boilerplate to get started with your module file: - function Queue(db, name) { - if (!(this instanceof Queue)) return new Queue(db, name) + function Queue(db) { + if (!(this instanceof Queue)) return new Queue(db) // ... } diff --git a/problems/queue/solution.js b/problems/queue/solution.js index b85741e..4b3a21c 100644 --- a/problems/queue/solution.js +++ b/problems/queue/solution.js @@ -1,19 +1,16 @@ -var sublevel = require('level-sublevel') -function Queue(db, name) { - if (!(this instanceof Queue)) return new Queue(db, name) +function Queue(db) { + if (!(this instanceof Queue)) return new Queue(db) - this._db = sublevel(db).sublevel(name) + this._db = db } -module.exports = Queue - Queue.prototype.push = function(element, callback) { var key = (new Date()).toISOString() this._db.put(key, element, callback) - return this; + return this } Queue.prototype.shift = function(callback) { @@ -31,7 +28,7 @@ Queue.prototype.shift = function(callback) { stream.once('error', callback) - return this; + return this } Queue.prototype.clear = function(callback) { @@ -41,5 +38,9 @@ Queue.prototype.clear = function(callback) { })) .on('close', callback) - return this; + return this } + +module.exports = Queue +// you can find an alternative implementation +// with more features in https://github.com/mcollina/level-queue-type From cc4ec1746c88b2bb0a78ead84169ecfefaab6422 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Sep 2013 09:27:38 +0200 Subject: [PATCH 4/5] Using Date.now() and process.hrtime() for high-resolution keys in queue. --- problems/queue/problem.txt | 7 +++---- problems/queue/solution.js | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/problems/queue/problem.txt b/problems/queue/problem.txt index e7bcd87..02ffc0e 100644 --- a/problems/queue/problem.txt +++ b/problems/queue/problem.txt @@ -54,9 +54,8 @@ Before coding, think how the queue data structure can be implemented on top of LevelUp. In particular, you want to "translate" a `push` into a level `put`, and `shift` into a limited ranged query. -All pushed element must be stored in separate keys. You might want to use -the current Date in the standard ISO date format (i.e. the format -generated by the Date object's `toISOString()` method.) as it provides -identifiers sorted in lexicographic order. +All pushed element must be stored in separate keys, by using a growing +monotonic identifier. What problems will you encounter if you use a counter? +Instead, try using `Date.now` and `process.hrtime()`. You can also use sublevel to handle the naming of the queues! diff --git a/problems/queue/solution.js b/problems/queue/solution.js index 4b3a21c..abe1baf 100644 --- a/problems/queue/solution.js +++ b/problems/queue/solution.js @@ -3,10 +3,11 @@ function Queue(db) { if (!(this instanceof Queue)) return new Queue(db) this._db = db + this._db._startTime = Date.now() } Queue.prototype.push = function(element, callback) { - var key = (new Date()).toISOString() + var key = [this._db._startTime].concat(process.hrtime()).join(';') this._db.put(key, element, callback) From 2e194b1733ae5fc0965838ff394673ad87ae9558 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 20 Sep 2013 17:24:45 +0200 Subject: [PATCH 5/5] More portable solution based on Date.now() Thanks to @juliangruber and @rvagg. --- problems/queue/problem.txt | 2 +- problems/queue/solution.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/problems/queue/problem.txt b/problems/queue/problem.txt index 02ffc0e..71a8d9e 100644 --- a/problems/queue/problem.txt +++ b/problems/queue/problem.txt @@ -56,6 +56,6 @@ level `put`, and `shift` into a limited ranged query. All pushed element must be stored in separate keys, by using a growing monotonic identifier. What problems will you encounter if you use a counter? -Instead, try using `Date.now` and `process.hrtime()`. +Instead, try using `Date.now()`. Is this the perfect solution? You can also use sublevel to handle the naming of the queues! diff --git a/problems/queue/solution.js b/problems/queue/solution.js index abe1baf..b01a9a9 100644 --- a/problems/queue/solution.js +++ b/problems/queue/solution.js @@ -3,11 +3,10 @@ function Queue(db) { if (!(this instanceof Queue)) return new Queue(db) this._db = db - this._db._startTime = Date.now() } Queue.prototype.push = function(element, callback) { - var key = [this._db._startTime].concat(process.hrtime()).join(';') + var key = Date.now() + '!' + Math.random().toString(16).slice(2); this._db.put(key, element, callback)