Repository: sanctuary-js/sanctuary (js)

Issue:
Add S.zip and S.zipWith
Create a type-safe version of [zip](http://ramdajs.com/0.22.1/docs/#zip) and [zipWith](http://ramdajs.com/0.22.1/docs/#zipWith).

Pull request description:
array: add S.zip and S.zipWith
Work in progress for zipWith!
I started with Ramda’s code (a single while) but switched to Z.reduce to take in count it’s capabilities over different types.

<del>Related to #337</del> <ins>Closes #337</ins>

Please give me feedback! I’m ready to improve this 🤗


Patch:
diff --git a/index.js b/index.js
index 2affa0e..5484619 100644
--- a/index.js
+++ b/index.js
@@ -216,6 +216,13 @@
   //  Fn :: (Type, Type) -> Type
   function Fn(x, y) { return $.Function([x, y]); }
 
+  //  pair :: a -> b -> Pair a b
+  function pair(x) {
+    return function(y) {
+      return [x, y];
+    };
+  }
+
   //  toObject :: a -> Object
   function toObject(x) {
     return x == null ? Object.create(null) : Object(x);
@@ -3586,6 +3593,57 @@
       [Fn(a, b), m(a), m(a)],
       Z.sortBy);
 
+  //# zip :: Array a -> Array b -> Array (Pair a b)
+  //.
+  //. Returns an array of pairs of corresponding elements from the given
+  //. arrays. The length of the resulting array is equal to the length of
+  //. the shorter input array.
+  //.
+  //. See also [`zipWith`](#zipWith).
+  //.
+  //. ```javascript
+  //. > S.zip(['a', 'b'], ['x', 'y', 'z'])
+  //. [['a', 'x'], ['b', 'y']]
+  //.
+  //. > S.zip([1, 3, 5], [2, 4])
+  //. [[1, 2], [3, 4]]
+  //. ```
+  function zip(xs, ys) {
+    return zipWith(pair, xs, ys);
+  }
+  S.zip =
+  def('zip',
+      {},
+      [$.Array(a), $.Array(b), $.Array($.Pair(a, b))],
+      zip);
+
+  //# zipWith :: (a -> b -> c) -> Array a -> Array b -> Array c
+  //.
+  //. Returns the result of combining, pairwise, the given arrays using the
+  //. given binary function. The length of the resulting array is equal to the
+  //. length of the shorter input array.
+  //.
+  //. See also [`zip`](#zip).
+  //.
+  //. ```javascript
+  //. > S.zipWith(a => b => a + b, ['a', 'b'], ['x', 'y', 'z'])
+  //. ['ax', 'by']
+  //.
+  //. > S.zipWith(a => b => [a, b], [1, 3, 5], [2, 4])
+  //. [[1, 2], [3, 4]]
+  //. ```
+  function zipWith(f, xs, ys) {
+    var result = [];
+    var len = Math.min(xs.length, ys.length);
+    for (var idx = 0; idx < len; idx += 1) result.push(f(xs[idx])(ys[idx]));
+    return result;
+  }
+  S.zipWith =
+  def('zipWith',
+      {},
+      [Fn(a, Fn(b, c)), $.Array(a), $.Array(b), $.Array(c)],
+      zipWith);
+
   //. ### Object
 
   //# prop :: String -> a -> b


Test patch:
diff --git a/test/internal/pair.js b/test/internal/pair.js
new file mode 100644
index 0000000..4154ec8
--- /dev/null
+++ b/test/internal/pair.js
@@ -0,0 +1,8 @@
+'use strict';
+
+//  pair :: a -> b -> Pair a b
+module.exports = function pair(x) {
+  return function(y) {
+    return [x, y];
+  };
+};
diff --git a/test/zip.js b/test/zip.js
new file mode 100644
index 0000000..71af435
--- /dev/null
+++ b/test/zip.js
@@ -0,0 +1,17 @@
+'use strict';
+
+var S = require('..');
+
+var eq = require('./internal/eq');
+
+
+test('zip', function() {
+
+  eq(typeof S.zip, 'function');
+  eq(S.zip.length, 2);
+  eq(S.zip.toString(), 'zip :: Array a -> Array b -> Array (Pair a b)');
+
+  eq(S.zip(['a', 'b'], ['x', 'y', 'z']), [['a', 'x'], ['b', 'y']]);
+  eq(S.zip([1, 3, 5], [2, 4]), [[1, 2], [3, 4]]);
+
+});
diff --git a/test/zipWith.js b/test/zipWith.js
new file mode 100644
index 0000000..eb5cd0b
--- /dev/null
+++ b/test/zipWith.js
@@ -0,0 +1,19 @@
+'use strict';
+
+var S = require('..');
+
+var concat = require('./internal/concat');
+var eq = require('./internal/eq');
+var pair = require('./internal/pair');
+
+
+test('zipWith', function() {
+
+  eq(typeof S.zipWith, 'function');
+  eq(S.zipWith.length, 3);
+  eq(S.zipWith.toString(), 'zipWith :: (a -> b -> c) -> Array a -> Array b -> Array c');
+
+  eq(S.zipWith(concat, ['a', 'b'], ['x', 'y', 'z']), ['ax', 'by']);
+  eq(S.zipWith(pair, [1, 3, 5], [2, 4]), [[1, 2], [3, 4]]);
+
+});