Repository: plaid/sanctuary
Pull request #48 (2015-06-08 20:31:06)

add drop and take functions
As discussed in #39 . There's a few parts of this PR that need clarifying. I'm not sure if I've got the type signature right and I didn't know whether to group the string and array tests separately. Also, should there be more tests and documentation around passing negative values to the function?

davidchambers: > Also, should there be more tests and documentation around passing negative values to the function?

I don't think we should document the handling of negative counts, as they're nonsensical. Some tests would be useful, though. How should we handle a negative count? Treat it as zero?

svozza: It should probably be treated the same way we treat a count that exceeds the length of the array and return `Nothing`.
davidchambers: That seems reasonable to me. Let's do that.


Patch:
diff --git a/index.js b/index.js
index 45250a5..e96e0c9 100644
--- a/index.js
+++ b/index.js
@@ -980,6 +980,48 @@
   //. ```
   S.init = S.slice(0, -1);
 
+  //# take :: Number -> [a] -> Maybe [a]
+  //.
+  //. Returns Just the first N elements of the given collection if N is
+  //. greater than or equal to zero and less than or equal to the length
+  //. of the collection; Nothing otherwise. Supports Array, String, and
+  //. any other collection type which provides a `slice` method.
+  //.
+  //. ```javascript
+  //. > S.take(2, ['a', 'b', 'c', 'd', 'e'])
+  //. Just(["a", "b"])
+  //.
+  //. > S.take(4, 'abcdefg')
+  //. Just("abcd")
+  //.
+  //. > S.take(4, ['a', 'b', 'c'])
+  //. Nothing()
+  //. ```
+  S.take = R.curry(function(n, xs) {
+    return n < 0 || R.eq(n, -0) ? Nothing() : slice(0, n, xs);
+  });
+
+  //# drop :: Number -> [a] -> Maybe [a]
+  //.
+  //. Returns Just all but the first N elements of the given collection
+  //. if N is greater than or equal to zero and less than or equal to the
+  //. length of the collection; Nothing otherwise. Supports Array, String,
+  //. and any other collection type which provides a `slice` method.
+  //.
+  //. ```javascript
+  //. > S.drop(2, ['a', 'b', 'c', 'd', 'e'])
+  //. Just(["c", "d", "e"])
+  //.
+  //. > S.drop(4, 'abcdefg')
+  //. Just("efg")
+  //.
+  //. > S.drop(4, 'abc')
+  //. Nothing()
+  //. ```
+  S.drop = R.curry(function(n, xs) {
+    return n < 0 || R.eq(n, -0) ? Nothing() : slice(n, -0, xs);
+  });
+
   //# find :: (a -> Boolean) -> [a] -> Maybe a
   //.
   //. Takes a predicate and a list and returns Just the leftmost element of


Test patch:
diff --git a/test/index.js b/test/index.js
index 823af2e..af35ec2 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1182,6 +1182,68 @@ describe('list', function() {
 
   });
 
+  describe('take', function() {
+
+    it('returns a Nothing if n is greater than collection length', function() {
+      eq(S.take(6, ['a', 'b', 'c', 'd', 'e']), S.Nothing());
+      eq(S.take(6, 'abcde'), S.Nothing());
+    });
+
+    it('returns a Nothing if n is negative', function() {
+      eq(S.take(-0, ['a', 'b', 'c', 'd', 'e']), S.Nothing());
+      eq(S.take(-1, ['a', 'b', 'c', 'd', 'e']), S.Nothing());
+      eq(S.take(-0, 'abcdefg'), S.Nothing());
+      eq(S.take(-1, 'abcde'), S.Nothing());
+    });
+
+    it('returns an empty collection if n is 0', function() {
+      eq(S.take(0, ['a', 'b', 'c', 'd', 'e']), S.Just([]));
+      eq(S.take(0, 'abcde'), S.Just(''));
+    });
+
+    it('returns Just the first two elements from the collection', function() {
+      eq(S.take(2, ['a', 'b', 'c', 'd', 'e']), S.Just(['a', 'b']));
+      eq(S.take(2, 'abcdefg'), S.Just('ab'));
+    });
+
+    it('returns Just the whole collection if n is equal to array length', function() {
+      eq(S.take(5, ['a', 'b', 'c', 'd', 'e']), S.Just(['a', 'b', 'c', 'd', 'e']));
+      eq(S.take(7, 'abcdefg'), S.Just('abcdefg'));
+    });
+
+  });
+
+  describe('drop', function() {
+
+    it('returns a Nothing if n is greater than collection length', function() {
+      eq(S.drop(6, ['a', 'b', 'c', 'd', 'e']), S.Nothing());
+      eq(S.drop(6, 'abcde'), S.Nothing());
+    });
+
+    it('returns a Nothing if n is negative', function() {
+      eq(S.drop(-3, ['a', 'b', 'c', 'd', 'e']), S.Nothing());
+      eq(S.drop(-0, ['a', 'b', 'c', 'd', 'e']), S.Nothing());
+      eq(S.drop(-3, 'abcde'), S.Nothing());
+      eq(S.drop(-0, 'abcde'), S.Nothing());
+    });
+
+    it('returns an empty collection if n is equal to collection length', function() {
+      eq(S.drop(5, ['a', 'b', 'c', 'd', 'e']), S.Just([]));
+      eq(S.drop(5, 'abcde'), S.Just(''));
+    });
+
+    it('returns Just the last three elements from the collection', function() {
+      eq(S.drop(2, ['a', 'b', 'c', 'd', 'e']), S.Just(['c', 'd', 'e']));
+      eq(S.drop(4, 'abcdefg'), S.Just('efg'));
+    });
+
+    it('returns Just the whole collection if n is zero', function() {
+      eq(S.drop(0, ['a', 'b', 'c', 'd', 'e']), S.Just(['a', 'b', 'c', 'd', 'e']));
+      eq(S.drop(0, 'abcdefg'), S.Just('abcdefg'));
+    });
+
+  });
+
   describe('find', function() {
 
     it('is a binary function', function() {