diff --git a/src/commands/index.js b/src/commands/index.js index 23028b0a6..6034c97bd 100644 --- a/src/commands/index.js +++ b/src/commands/index.js @@ -44,6 +44,7 @@ export * from './lpop'; export * from './lpopBuffer'; export * from './lpush'; export * from './lpushx'; +export * from './lrange'; export * from './lrem'; export * from './lset'; export * from './mget'; diff --git a/src/commands/lrange.js b/src/commands/lrange.js new file mode 100644 index 000000000..e20962841 --- /dev/null +++ b/src/commands/lrange.js @@ -0,0 +1,27 @@ +/** + * Returns the specified elements of the list stored at key. The offsets start and stop are zero-based indexes, with 0 being the first element of the list (the head of the list), 1 being the next element and so on. + * These offsets can also be negative numbers indicating offsets starting at the end of the list. For example, -1 is the last element of the list, -2 the penultimate, and so on. + * + * @param {string} key + * @param {string} start Start index + * @param {string} end End index (included in returned range) + * @return {Array} An array in the defined range + */ +export function lrange(key, s, e) { + if (this.data.has(key) && !(this.data.get(key) instanceof Array)) { + throw new Error(`Key ${key} does not contain a list`); + } + let start = parseInt(s, 10); + let end = parseInt(e, 10); + + const list = this.data.get(key) || []; + + if (start < 0) { + start = list.length + start; + } + if (end < 0) { + end = list.length + end; + } + + return list.slice(start, end + 1); +} diff --git a/test/commands/lrange.js b/test/commands/lrange.js new file mode 100644 index 000000000..3cf9e2a4f --- /dev/null +++ b/test/commands/lrange.js @@ -0,0 +1,67 @@ +import expect from 'expect'; + +import MockRedis from '../../src'; + +describe('lrange', () => { + it('should return first 3 items', () => { + const redis = new MockRedis({ + data: { + foo: ['1', '2', '3', '4', '5'], + }, + }); + + return redis + .lrange('foo', 0, 2) + .then(res => expect(res).toEqual(['1', '2', '3'])); + }); + + it('should return last 3 items', () => { + const redis = new MockRedis({ + data: { + foo: ['1', '2', '3', '4', '5'], + }, + }); + + return redis + .lrange('foo', -3, -1) + .then(res => expect(res).toEqual(['3', '4', '5'])); + }); + + it('should return last all items on larger numbers', () => { + const redis = new MockRedis({ + data: { + foo: ['1', '2', '3', '4', '5'], + }, + }); + + return redis + .lrange('foo', 0, 100) + .then(res => expect(res).toEqual(['1', '2', '3', '4', '5'])); + }); + + it('should return empty array if out-of-range', () => { + const redis = new MockRedis({ + data: { + foo: ['1', '2', '3', '4', '5'], + }, + }); + + return redis + .lrange('foo', 10, 100) + .then(res => expect(res).toEqual([])); + }); + + it('should throw an exception if the key contains something other than a list', () => { + const redis = new MockRedis({ + data: { + foo: 'not a list', + }, + }); + + return redis + .lrange('foo', 0, 2) + .catch(err => + expect(err.message).toBe('Key foo does not contain a list') + ); + }); +});