From 7d636b41cd3d931e06a0783c1ff0983012fff882 Mon Sep 17 00:00:00 2001 From: Stefano Vozza Date: Sun, 8 May 2016 23:04:35 +0100 Subject: [PATCH] add S.keys, S.values and S.pairs --- index.js | 48 ++++++++++++++++++++++++++++++++++++++ test/keys.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++ test/pairs.js | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/values.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 229 insertions(+) create mode 100644 test/keys.js create mode 100644 test/pairs.js create mode 100644 test/values.js diff --git a/index.js b/index.js index b0dcde5d..aa39e984 100644 --- a/index.js +++ b/index.js @@ -27,6 +27,8 @@ //. //. R.map(S.toUpper, S.head(words)) //. +//. Sanctuary is designed to work in Node.js and in ES5-compatible browsers. +//. //. ## Types //. //. Sanctuary uses Haskell-like type signatures to describe the types of @@ -2746,6 +2748,52 @@ return filter(is(type), Just(x)); }); + //# keys :: StrMap a -> Array String + //. + //. Returns the keys of the given string map, in arbitrary order. + //. + //. ```javascript + //. > S.keys({b: 2, c: 3, a: 1}).sort() + //. ['a', 'b', 'c'] + //. ``` + S.keys = + def('keys', + {}, + [$.StrMap(a), $.Array($.String)], + Object.keys); + + //# values :: StrMap a -> Array a + //. + //. Returns the values of the given string map, in arbitrary order. + //. + //. ```javascript + //. > S.values({a: 1, c: 3, b: 2}).sort() + //. [1, 2, 3] + //. ``` + S.values = + def('values', + {}, + [$.StrMap(a), $.Array(a)], + function(strMap) { + return Object.keys(strMap).map(function(key) { return strMap[key]; }); + }); + + //# toPairs :: StrMap a -> Array (Pair String a) + //. + //. Returns the key–value pairs of the given string map, in arbitrary order. + //. + //. ```javascript + //. > S.pairs({b: 2, a: 1, c: 3}).sort() + //. [['a', 1], ['b', 2], ['c', 3]] + //. ``` + S.pairs = + def('pairs', + {}, + [$.StrMap(a), $.Array($.Pair($.String, a))], + function(strMap) { + return Object.keys(strMap).map(function(k) { return [k, strMap[k]]; }); + }); + //. ### Number //# negate :: ValidNumber -> ValidNumber diff --git a/test/keys.js b/test/keys.js new file mode 100644 index 00000000..b8f6eaf5 --- /dev/null +++ b/test/keys.js @@ -0,0 +1,59 @@ +'use strict'; + +var throws = require('assert').throws; + +var eq = require('./utils').eq; +var errorEq = require('./utils').errorEq; +var S = require('..'); + + +describe('keys', function() { + + it('is a unary function', function() { + eq(typeof S.keys, 'function'); + eq(S.keys.length, 1); + }); + + it('type checks its arguments', function() { + throws(function() { S.keys('xxx'); }, + errorEq(TypeError, + 'Invalid value\n' + + '\n' + + 'keys :: StrMap a -> Array String\n' + + ' ^^^^^^^^\n' + + ' 1\n' + + '\n' + + '1) "xxx" :: String\n' + + '\n' + + 'The value at position 1 is not a member of ‘StrMap a’.\n')); + + throws(function() { S.keys({a: '1', b: 2, c: '3'}); }, + errorEq(TypeError, + 'Type-variable constraint violation\n' + + '\n' + + 'keys :: StrMap a -> Array String\n' + + ' ^\n' + + ' 1\n' + + '\n' + + '1) "1" :: String\n' + + ' 2 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber\n' + + ' "3" :: String\n' + + '\n' + + 'Since there is no type of which all the above values are members, the type-variable constraint has been violated.\n')); + }); + + it("returns an array of the given object's own keys", function() { + eq(S.keys({}), []); + eq(S.keys({a: 1, b: 2, c: 3}).sort(), ['a', 'b', 'c']); + }); + + it('does not include prototype properties', function() { + var proto = {a: 1, b: 2}; + var obj = Object.create(proto); + obj.c = 3; + obj.d = 4; + + eq(S.keys(obj).sort(), ['c', 'd']); + }); + +}); diff --git a/test/pairs.js b/test/pairs.js new file mode 100644 index 00000000..edb80c6d --- /dev/null +++ b/test/pairs.js @@ -0,0 +1,63 @@ +'use strict'; + +var throws = require('assert').throws; + +var eq = require('./utils').eq; +var errorEq = require('./utils').errorEq; +var S = require('..'); + + +describe('toPairs', function() { + + var comparePairsAsc = function(a, b) { + return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0; + }; + + it('is a unary function', function() { + eq(typeof S.pairs, 'function'); + eq(S.pairs.length, 1); + }); + + it('type checks its arguments', function() { + throws(function() { S.pairs('xxx'); }, + errorEq(TypeError, + 'Invalid value\n' + + '\n' + + 'pairs :: StrMap a -> Array (Pair String a)\n' + + ' ^^^^^^^^\n' + + ' 1\n' + + '\n' + + '1) "xxx" :: String\n' + + '\n' + + 'The value at position 1 is not a member of ‘StrMap a’.\n')); + + throws(function() { S.pairs({a: '1', b: 2, c: '3'}); }, + errorEq(TypeError, + 'Type-variable constraint violation\n' + + '\n' + + 'pairs :: StrMap a -> Array (Pair String a)\n' + + ' ^\n' + + ' 1\n' + + '\n' + + '1) "1" :: String\n' + + ' 2 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber\n' + + ' "3" :: String\n' + + '\n' + + 'Since there is no type of which all the above values are members, the type-variable constraint has been violated.\n')); + }); + + it('returns an array with the key value pairs of each property of the object', function() { + eq(S.pairs({}), []); + eq(S.pairs({a: 1, b: 2, c: 3}).sort(comparePairsAsc), [['a', 1], ['b', 2], ['c', 3]]); + }); + + it('does not include prototype properties', function() { + var proto = {a: 1, b: 2}; + var obj = Object.create(proto); + obj.c = 3; + obj.d = 4; + + eq(S.pairs(obj).sort(comparePairsAsc), [['c', 3], ['d', 4]]); + }); + +}); diff --git a/test/values.js b/test/values.js new file mode 100644 index 00000000..d01bb431 --- /dev/null +++ b/test/values.js @@ -0,0 +1,59 @@ +'use strict'; + +var throws = require('assert').throws; + +var eq = require('./utils').eq; +var errorEq = require('./utils').errorEq; +var S = require('..'); + + +describe('values', function() { + + it('is a unary function', function() { + eq(typeof S.values, 'function'); + eq(S.values.length, 1); + }); + + it('type checks its arguments', function() { + throws(function() { S.values('xxx'); }, + errorEq(TypeError, + 'Invalid value\n' + + '\n' + + 'values :: StrMap a -> Array a\n' + + ' ^^^^^^^^\n' + + ' 1\n' + + '\n' + + '1) "xxx" :: String\n' + + '\n' + + 'The value at position 1 is not a member of ‘StrMap a’.\n')); + + throws(function() { S.values({a: '1', b: 2, c: '3'}); }, + errorEq(TypeError, + 'Type-variable constraint violation\n' + + '\n' + + 'values :: StrMap a -> Array a\n' + + ' ^\n' + + ' 1\n' + + '\n' + + '1) "1" :: String\n' + + ' 2 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber\n' + + ' "3" :: String\n' + + '\n' + + 'Since there is no type of which all the above values are members, the type-variable constraint has been violated.\n')); + }); + + it("returns an array of the given object's own values", function() { + eq(S.values({}), []); + eq(S.values({a: 1, b: 2, c: 3}).sort(), [1, 2, 3]); + }); + + it('does not include prototype values', function() { + var proto = {a: 1, b: 2}; + var obj = Object.create(proto); + obj.c = 3; + obj.d = 4; + + eq(S.values(obj).sort(), [3, 4]); + }); + +});