├── .npmrc ├── .eslintrc ├── test ├── helpers │ ├── setup-browser-env.js │ └── dummyData.js ├── toNull.js ├── toUndefined.js ├── toError.js ├── toFunction.js ├── toPromise.js ├── toGenerator.js ├── toSymbol.js ├── toRegExp.js ├── toBoolean.js ├── toSet.js ├── toArray.js ├── toObject.js ├── toMap.js ├── toDate.js ├── toNumber.js ├── _internals │ └── utils.js └── toString.js ├── .gitignore ├── src ├── toNull.js ├── toUndefined.js ├── toFunction.js ├── toPromise.js ├── toError.js ├── toSymbol.js ├── toBoolean.js ├── toRegExp.js ├── toArray.js ├── toSet.js ├── toMap.js ├── _internals │ ├── numbers.js │ ├── objectClass.js │ └── utils.js ├── index.js ├── toObject.js ├── toGenerator.js ├── toDate.js ├── toString.js └── toNumber.js ├── .npmignore ├── webpack.config.minified.js ├── CHANGELOG.md ├── webpack.config.dev.js ├── LICENSE ├── webpack.config.js ├── .babelrc ├── package.json ├── README.md ├── DEV_ONLY └── App.js └── API.md /.npmrc: -------------------------------------------------------------------------------- 1 | scripts-prepend-node-path=true -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["rapid7/browser"], 3 | "rules": {} 4 | } 5 | -------------------------------------------------------------------------------- /test/helpers/setup-browser-env.js: -------------------------------------------------------------------------------- 1 | const browserEnv = require('browser-env'); 2 | 3 | browserEnv(); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .nyc_output 3 | .rpt2_cache 4 | coverage 5 | dist 6 | es 7 | lib 8 | node_modules 9 | *.log 10 | -------------------------------------------------------------------------------- /src/toNull.js: -------------------------------------------------------------------------------- 1 | /** 2 | * return null (noop) 3 | * 4 | * @returns {null} 5 | */ 6 | const toNull = () => null; 7 | 8 | export default toNull; 9 | -------------------------------------------------------------------------------- /src/toUndefined.js: -------------------------------------------------------------------------------- 1 | /** 2 | * return undefined (noop) 3 | * 4 | * @returns {undefined} 5 | */ 6 | const toUndefined = () => void 0; 7 | 8 | export default toUndefined; 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .eslintrc 3 | .git 4 | .gitignore 5 | .idea 6 | .npmignore 7 | .nyc_output 8 | .rpt2_cache 9 | __tests__ 10 | benchmarks 11 | coverage 12 | DEV_ONLY 13 | node_modules 14 | src 15 | test 16 | webpack 17 | *.log 18 | yarn.lock 19 | -------------------------------------------------------------------------------- /test/toNull.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toNull from '../src/toNull'; 4 | 5 | import * as dummyData from './helpers/dummyData'; 6 | 7 | test('if all executions of toNull yield null', (t) => { 8 | for (let key in dummyData) { 9 | t.is(toNull(dummyData[key]), null); 10 | } 11 | }); -------------------------------------------------------------------------------- /webpack.config.minified.js: -------------------------------------------------------------------------------- 1 | const defaultConfig = require('./webpack.config'); 2 | 3 | module.exports = Object.assign({}, defaultConfig, { 4 | devtool: undefined, 5 | 6 | mode: 'production', 7 | 8 | output: Object.assign({}, defaultConfig.output, { 9 | filename: 'convertify.min.js', 10 | }), 11 | }); 12 | -------------------------------------------------------------------------------- /test/toUndefined.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toUndefined from '../src/toUndefined'; 4 | 5 | import * as dummyData from './helpers/dummyData'; 6 | 7 | test('if all executions of toUndefined yield undefined', (t) => { 8 | for (let key in dummyData) { 9 | t.is(toUndefined(dummyData[key]), undefined); 10 | } 11 | }); -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # convertify CHANGELOG 2 | 3 | ## 1.0.4 4 | 5 | - Fix security issue related to old version of `webpack-dev-server` 6 | - Leverage `fast-stringify` for handling circular objects when stringifying 7 | 8 | ## 1.0.3 9 | 10 | - Fix description in `package.json` 11 | 12 | ## 1.0.2 13 | 14 | - Fix JSONified strings as object keys not being parsed when converted to maps 15 | 16 | ## 1.0.1 17 | 18 | - Bump version for README fixes 19 | 20 | ## 1.0.0 21 | 22 | - Initial release 23 | -------------------------------------------------------------------------------- /src/toFunction.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import {FUNCTION} from './_internals/objectClass'; 3 | 4 | // utils 5 | import {getObjectClass} from './_internals/utils'; 6 | 7 | /** 8 | * convert object to function by wrapping the object 9 | * in a function 10 | * 11 | * @param {any} object 12 | * @returns {function} 13 | */ 14 | const toFunction = (object) => 15 | getObjectClass(object) === FUNCTION 16 | ? object 17 | : function() { 18 | return object; 19 | }; 20 | 21 | export default toFunction; 22 | -------------------------------------------------------------------------------- /test/toError.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toError from '../src/toError'; 4 | import toString from '../src/toString'; 5 | 6 | import * as dummyData from './helpers/dummyData'; 7 | 8 | test('if passing an error returns the same object', (t) => { 9 | t.is(toError(dummyData.ERROR), dummyData.ERROR); 10 | }); 11 | 12 | test('if passing values yield the stringified object wrapped in an error', (t) => { 13 | for (let key in dummyData) { 14 | if (key !== 'ERROR') { 15 | t.deepEqual(toError(dummyData[key]), new Error(toString(dummyData[key]))); 16 | } 17 | } 18 | }); -------------------------------------------------------------------------------- /src/toPromise.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import {PROMISE} from './_internals/objectClass'; 3 | 4 | // utils 5 | import { 6 | getObjectClass, 7 | throwUnsupportedError, 8 | } from './_internals/utils'; 9 | 10 | /** 11 | * convert object to promise by wrapping it in 12 | * a Promise.resolve() 13 | * 14 | * @param {any} object 15 | * @returns {promise} 16 | */ 17 | const toPromise = (object) => { 18 | if (typeof Promise === 'undefined') { 19 | throwUnsupportedError('Promise'); 20 | } 21 | 22 | return getObjectClass(object) === PROMISE ? object : Promise.resolve(object); 23 | }; 24 | 25 | export default toPromise; 26 | -------------------------------------------------------------------------------- /webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | const PORT = 3000; 5 | 6 | const defaultConfig = require('./webpack.config'); 7 | 8 | module.exports = Object.assign({}, defaultConfig, { 9 | devServer: { 10 | contentBase: './dist', 11 | host: 'localhost', 12 | inline: true, 13 | lazy: false, 14 | noInfo: false, 15 | port: PORT, 16 | quiet: false, 17 | stats: { 18 | colors: true, 19 | progress: true, 20 | }, 21 | }, 22 | 23 | entry: [path.resolve(__dirname, 'DEV_ONLY', 'App.js')], 24 | 25 | plugins: defaultConfig.plugins.concat([new HtmlWebpackPlugin()]), 26 | }); 27 | -------------------------------------------------------------------------------- /test/toFunction.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toFunction from '../src/toFunction'; 4 | 5 | import * as dummyData from './helpers/dummyData'; 6 | 7 | const testFunctionWrapperResult = (t, value) => { 8 | const result = toFunction(value); 9 | 10 | t.is(typeof result, 'function'); 11 | 12 | t.deepEqual(result(), value); 13 | }; 14 | 15 | test('if passing an error returns the same object', (t) => { 16 | t.is(toFunction(dummyData.FUNCTION), dummyData.FUNCTION); 17 | }); 18 | 19 | test('if toFunction calls yield the object wrapped in a function', (t) => { 20 | for (let key in dummyData) { 21 | if (key !== 'FUNCTION') { 22 | testFunctionWrapperResult(t, dummyData.key); 23 | } 24 | } 25 | }); -------------------------------------------------------------------------------- /src/toError.js: -------------------------------------------------------------------------------- 1 | // transform 2 | import toString from './toString'; 3 | 4 | // constants 5 | import { 6 | ERROR, 7 | STRING, 8 | } from './_internals/objectClass'; 9 | 10 | // utils 11 | import {getObjectClass} from './_internals/utils'; 12 | 13 | /** 14 | * convert object to error with the stringified 15 | * contents of the object as the message 16 | * 17 | * @param {any} object 18 | * @returns {error} 19 | */ 20 | const toError = (object) => { 21 | const objectClass = getObjectClass(object); 22 | 23 | if (objectClass === ERROR) { 24 | return object; 25 | } 26 | 27 | const message = objectClass === STRING ? object : toString(object); 28 | 29 | return new Error(message); 30 | }; 31 | 32 | export default toError; 33 | -------------------------------------------------------------------------------- /test/toPromise.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toPromise from '../src/toPromise'; 4 | 5 | import * as dummyData from './helpers/dummyData'; 6 | 7 | const testFunctionWrapperResult = async (t, value) => { 8 | const result = toPromise(value); 9 | 10 | t.is(typeof result, 'object'); 11 | 12 | const resultValue = await result; 13 | 14 | t.deepEqual(resultValue, value); 15 | }; 16 | 17 | test('if passing an error returns the same object', (t) => { 18 | t.is(toPromise(dummyData.PROMISE), dummyData.PROMISE); 19 | }); 20 | 21 | test('if toPromise calls yield the object wrapped in a function', async (t) => { 22 | for (let key in dummyData) { 23 | if (key !== 'PROMISE') { 24 | await testFunctionWrapperResult(t, dummyData[key]); 25 | } 26 | } 27 | }); -------------------------------------------------------------------------------- /test/toGenerator.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toGenerator from '../src/toGenerator'; 4 | 5 | import * as dummyData from './helpers/dummyData'; 6 | 7 | const testGeneratorWrapperResult = (t, value) => { 8 | const result = toGenerator(value); 9 | 10 | t.is(typeof result, 'function'); 11 | 12 | const iterator = result(); 13 | 14 | try { 15 | iterator.next(); 16 | t.pass(); 17 | } catch (exception) { 18 | t.fail(); 19 | } 20 | }; 21 | 22 | test('if passing an error returns the same object', (t) => { 23 | t.is(toGenerator(dummyData.GENERATOR), dummyData.GENERATOR); 24 | }); 25 | 26 | test('if toGenerator calls yield the object wrapped in a function', (t) => { 27 | for (let key in dummyData) { 28 | if (key !== 'GENERATOR') { 29 | testGeneratorWrapperResult(t, dummyData[key]); 30 | } 31 | } 32 | }); -------------------------------------------------------------------------------- /src/toSymbol.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | STRING, 4 | SYMBOL, 5 | } from './_internals/objectClass'; 6 | 7 | // transform 8 | import toString from './toString'; 9 | 10 | // utils 11 | import { 12 | getObjectClass, 13 | throwUnsupportedError, 14 | } from './_internals/utils'; 15 | 16 | /** 17 | * convert object to symbol by creating a new symbol 18 | * from the stringified object 19 | * 20 | * @param {any} object 21 | * @returns {symbol} 22 | */ 23 | const toSymbol = (object) => { 24 | if (typeof Symbol === 'undefined') { 25 | throwUnsupportedError('Symbol'); 26 | } 27 | 28 | const objectClass = getObjectClass(object); 29 | 30 | if (objectClass === SYMBOL) { 31 | return object; 32 | } 33 | 34 | const string = objectClass === STRING ? object : toString(object); 35 | 36 | return Symbol(string); 37 | }; 38 | 39 | export default toSymbol; 40 | -------------------------------------------------------------------------------- /src/toBoolean.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | ARRAY, 4 | BOOLEAN, 5 | MAP, 6 | OBJECT, 7 | SET, 8 | } from './_internals/objectClass'; 9 | 10 | // utils 11 | import {getObjectClass} from './_internals/utils'; 12 | 13 | const {keys} = Object; 14 | 15 | /** 16 | * convert object to boolean with different mappings 17 | * based on object type 18 | * 19 | * @param {any} object 20 | * @returns {boolean} 21 | */ 22 | const toBoolean = (object) => { 23 | const objectClass = getObjectClass(object); 24 | 25 | if (objectClass === BOOLEAN) { 26 | return object; 27 | } 28 | 29 | if (objectClass === ARRAY) { 30 | return !!object.length; 31 | } 32 | 33 | if (objectClass === OBJECT) { 34 | return !!keys(object).length; 35 | } 36 | 37 | if (objectClass === MAP || objectClass === SET) { 38 | return !!object.size; 39 | } 40 | 41 | return !!object; 42 | }; 43 | 44 | export default toBoolean; 45 | -------------------------------------------------------------------------------- /src/toRegExp.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | REGEXP, 4 | STRING, 5 | } from './_internals/objectClass'; 6 | 7 | // transform 8 | import toString from './toString'; 9 | 10 | // utils 11 | import { 12 | getObjectClass, 13 | isEncapsulatedBy, 14 | } from './_internals/utils'; 15 | 16 | /** 17 | * convert object to regexp with different mappings 18 | * based on object type 19 | * 20 | * @param {any} object 21 | * @returns {regexp} 22 | */ 23 | const toRegExp = (object) => { 24 | const objectToString = getObjectClass(object); 25 | 26 | if (objectToString === REGEXP) { 27 | return object; 28 | } 29 | 30 | let string = objectToString === STRING ? object : toString(object); 31 | 32 | if (isEncapsulatedBy(string, '/')) { 33 | string = string.substring(1, string.length - 1); 34 | } 35 | 36 | if (!string) { 37 | string = '(.*)'; 38 | } 39 | 40 | return new RegExp(string); 41 | }; 42 | 43 | export default toRegExp; 44 | -------------------------------------------------------------------------------- /test/helpers/dummyData.js: -------------------------------------------------------------------------------- 1 | export const ARRAY = [ 2 | 'foo', 3 | 'bar' 4 | ]; 5 | export const BOOLEAN = true; 6 | export const DATE = new Date(); 7 | export const ERROR = new Error('foo'); 8 | export const FUNCTION = (foo) => { 9 | return foo; 10 | }; 11 | export const GENERATOR = function* (foo) { 12 | yield foo; 13 | }; 14 | export const MAP = new Map().set('foo', 'bar').set('bar', 'baz'); 15 | export const NULL = null; 16 | export const NUMBER = 123; 17 | export const OBJECT = { 18 | foo: 'bar', 19 | bar: 'baz' 20 | }; 21 | export const PROMISE = Promise.resolve('foo'); 22 | export const REGEXP = /foo/; 23 | export const SET = new Set().add('foo').add('bar'); 24 | export const STRING = 'foo'; 25 | export const SYMBOL = Symbol('foo'); 26 | export const UNDEFINED = undefined; 27 | 28 | export const EMPTY_ARRAY = []; 29 | export const EMPTY_OBJECT = {}; 30 | export const EMPTY_MAP = new Map(); 31 | export const EMPTY_SET = new Set(); 32 | export const EMPTY_STRING = ''; -------------------------------------------------------------------------------- /test/toSymbol.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toString from '../src/toString'; 4 | import toSymbol from '../src/toSymbol'; 5 | 6 | import * as dummyData from './helpers/dummyData'; 7 | 8 | const KEYS_TO_SKIP_FOR_TOSTRING = [ 9 | 'EMPTY_STRING', 10 | 'STRING', 11 | 'SYMBOL' 12 | ]; 13 | 14 | test('if passing a symbol returns the same object', (t) => { 15 | t.is(toSymbol(dummyData.SYMBOL), dummyData.SYMBOL); 16 | }); 17 | 18 | test('if passing a string value returns the correct symbol', (t) => { 19 | t.is(toSymbol(dummyData.EMPTY_STRING).toString(), Symbol(dummyData.EMPTY_STRING).toString()); 20 | t.is(toSymbol(dummyData.STRING).toString(), Symbol(dummyData.STRING).toString()); 21 | }); 22 | 23 | test('if passing a stringified value of the object returns the correct symbol', (t) => { 24 | for (let key in dummyData) { 25 | if (!~KEYS_TO_SKIP_FOR_TOSTRING.indexOf(key)) { 26 | t.is(toSymbol(dummyData[key]).toString(), Symbol(toString(dummyData[key])).toString()); 27 | } 28 | } 29 | }); -------------------------------------------------------------------------------- /test/toRegExp.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toString from '../src/toString'; 4 | import toRegExp from '../src/toRegExp'; 5 | 6 | import * as dummyData from './helpers/dummyData'; 7 | 8 | const EMPTY_REGEXP = /(.*)/; 9 | 10 | const KEYS_TO_SKIP_FOR_TOSTRING = [ 11 | 'EMPTY_STRING', 12 | 'STRING', 13 | 'SYMBOL', 14 | 'REGEXP' 15 | ]; 16 | 17 | test('if passing a regexp returns the same object', (t) => { 18 | t.is(toRegExp(dummyData.REGEXP), dummyData.REGEXP); 19 | }); 20 | 21 | test('if passing a string value returns the correct RegExp value without stringification needed', (t) => { 22 | t.deepEqual(toRegExp(dummyData.EMPTY_STRING), EMPTY_REGEXP); 23 | t.deepEqual(toRegExp(dummyData.STRING), new RegExp(dummyData.STRING)); 24 | }); 25 | 26 | test('if passing a stringified value of the object returns the correct new RegExp', (t) => { 27 | for (let key in dummyData) { 28 | if (!~KEYS_TO_SKIP_FOR_TOSTRING.indexOf(key)) { 29 | t.deepEqual(toRegExp(dummyData[key]), new RegExp(toString(dummyData[key]))); 30 | } 31 | } 32 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Tony Quetano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = { 5 | devtool: '#source-map', 6 | 7 | entry: [path.resolve(__dirname, 'src', 'index.js')], 8 | 9 | mode: 'development', 10 | 11 | module: { 12 | rules: [ 13 | { 14 | enforce: 'pre', 15 | include: [path.resolve(__dirname, 'src')], 16 | loader: 'eslint-loader', 17 | options: { 18 | configFile: '.eslintrc', 19 | failOnError: true, 20 | failOnWarning: false, 21 | formatter: require('eslint-friendly-formatter'), 22 | }, 23 | test: /\.js$/, 24 | }, 25 | { 26 | include: [path.resolve(__dirname, 'src'), path.resolve(__dirname, 'DEV_ONLY')], 27 | loader: 'babel-loader', 28 | test: /\.js$/, 29 | }, 30 | ], 31 | }, 32 | 33 | output: { 34 | filename: 'convertify.js', 35 | library: 'convertify', 36 | libraryTarget: 'umd', 37 | path: path.resolve(__dirname, 'dist'), 38 | umdNamedDefine: true, 39 | }, 40 | 41 | plugins: [new webpack.EnvironmentPlugin(['NODE_ENV'])], 42 | }; 43 | -------------------------------------------------------------------------------- /src/toArray.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | ARRAY, 4 | OBJECT, 5 | MAP, 6 | SET, 7 | STRING, 8 | } from './_internals/objectClass'; 9 | 10 | // utils 11 | import { 12 | getObjectClass, 13 | isEncapsulatedBy, 14 | parse, 15 | } from './_internals/utils'; 16 | 17 | /** 18 | * convert object to an array, with different 19 | * mappings depending on the object type 20 | * 21 | * @param {any} object 22 | * @returns {array} 23 | */ 24 | const toArray = (object) => { 25 | const objectClass = getObjectClass(object); 26 | 27 | if (objectClass === ARRAY) { 28 | return object; 29 | } 30 | 31 | if (objectClass === OBJECT) { 32 | const array = []; 33 | 34 | for (let key in object) { 35 | array.push(object[key]); 36 | } 37 | 38 | return array; 39 | } 40 | 41 | if (objectClass === MAP || objectClass === SET) { 42 | const array = []; 43 | 44 | object.forEach((value) => array.push(value)); 45 | 46 | return array; 47 | } 48 | 49 | if (objectClass === STRING) { 50 | return isEncapsulatedBy(object, '[', ']') ? parse(object, () => [object]) : [object]; 51 | } 52 | 53 | return [object]; 54 | }; 55 | 56 | export default toArray; 57 | -------------------------------------------------------------------------------- /src/toSet.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | ARRAY, 4 | MAP, 5 | SET, 6 | OBJECT, 7 | } from './_internals/objectClass'; 8 | 9 | // utils 10 | import { 11 | getObjectClass, 12 | throwUnsupportedError, 13 | } from './_internals/utils'; 14 | 15 | /** 16 | * convert object to set with different mappings 17 | * based on object type 18 | * 19 | * @param {any} object 20 | * @returns {set} 21 | */ 22 | const toSet = (object) => { 23 | if (typeof Set === 'undefined') { 24 | throwUnsupportedError('Set'); 25 | } 26 | 27 | const objectClass = getObjectClass(object); 28 | 29 | if (objectClass === SET) { 30 | return object; 31 | } 32 | 33 | if (objectClass === ARRAY) { 34 | return object.reduce((set, value) => { 35 | set.add(value); 36 | 37 | return set; 38 | }, new Set()); 39 | } 40 | 41 | if (objectClass === MAP) { 42 | if (typeof Map === 'undefined') { 43 | throwUnsupportedError('Map'); 44 | } 45 | 46 | const set = new Set(); 47 | 48 | object.forEach((value) => set.add(value)); 49 | 50 | return set; 51 | } 52 | 53 | if (objectClass === OBJECT) { 54 | const set = new Set(); 55 | 56 | for (let key in object) { 57 | set.add(object[key]); 58 | } 59 | 60 | return set; 61 | } 62 | 63 | const set = new Set(); 64 | 65 | set.add(object); 66 | 67 | return set; 68 | }; 69 | 70 | export default toSet; 71 | -------------------------------------------------------------------------------- /test/toBoolean.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toBoolean from '../src/toBoolean'; 4 | 5 | import { 6 | ARRAY, 7 | BOOLEAN, 8 | DATE, 9 | EMPTY_ARRAY, 10 | EMPTY_MAP, 11 | EMPTY_OBJECT, 12 | EMPTY_SET, 13 | EMPTY_STRING, 14 | ERROR, 15 | FUNCTION, 16 | GENERATOR, 17 | MAP, 18 | NULL, 19 | NUMBER, 20 | OBJECT, 21 | PROMISE, 22 | REGEXP, 23 | SET, 24 | STRING, 25 | SYMBOL, 26 | UNDEFINED 27 | } from './helpers/dummyData'; 28 | 29 | test('if passing a boolean returns the same object', (t) => { 30 | t.is(toBoolean(BOOLEAN), BOOLEAN); 31 | }); 32 | 33 | test('if passing implicitly coerced values yield the correct result', (t) => { 34 | t.false(toBoolean(EMPTY_STRING)); 35 | t.true(toBoolean(ERROR)); 36 | t.true(toBoolean(FUNCTION)); 37 | t.true(toBoolean(GENERATOR)); 38 | t.false(toBoolean(NULL)); 39 | t.true(toBoolean(NUMBER)); 40 | t.true(toBoolean(PROMISE)); 41 | t.true(toBoolean(REGEXP)); 42 | t.true(toBoolean(STRING)); 43 | t.true(toBoolean(SYMBOL)); 44 | t.false(toBoolean(UNDEFINED)); 45 | }); 46 | 47 | test('if passing mapped values based on values existing yield the correct result', (t) => { 48 | t.true(toBoolean(ARRAY)); 49 | t.true(toBoolean(DATE)); 50 | t.false(toBoolean(EMPTY_ARRAY)); 51 | t.false(toBoolean(EMPTY_MAP)); 52 | t.false(toBoolean(EMPTY_OBJECT)); 53 | t.false(toBoolean(EMPTY_SET)); 54 | t.true(toBoolean(MAP)); 55 | t.true(toBoolean(OBJECT)); 56 | t.true(toBoolean(SET)); 57 | }); -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "development": { 4 | "plugins": ["add-module-exports"], 5 | "presets": [ 6 | [ 7 | "env", 8 | { 9 | "loose": true, 10 | "modules": false 11 | } 12 | ], 13 | "react", 14 | "stage-2" 15 | ] 16 | }, 17 | "es": { 18 | "plugins": ["add-module-exports"], 19 | "presets": [ 20 | [ 21 | "env", 22 | { 23 | "loose": true, 24 | "modules": false 25 | } 26 | ], 27 | "react", 28 | "stage-2" 29 | ] 30 | }, 31 | "lib": { 32 | "plugins": ["add-module-exports"], 33 | "presets": [ 34 | [ 35 | "env", 36 | { 37 | "loose": true 38 | } 39 | ], 40 | "react", 41 | "stage-2" 42 | ] 43 | }, 44 | "production": { 45 | "plugins": ["add-module-exports"], 46 | "presets": [ 47 | [ 48 | "env", 49 | { 50 | "loose": true, 51 | "modules": false 52 | } 53 | ], 54 | "react", 55 | "stage-2" 56 | ] 57 | }, 58 | "test": { 59 | "plugins": ["add-module-exports"], 60 | "presets": [ 61 | [ 62 | "env", 63 | { 64 | "loose": true 65 | } 66 | ], 67 | "react", 68 | "stage-2" 69 | ] 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/toMap.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | ARRAY, 4 | MAP, 5 | OBJECT, 6 | SET, 7 | } from './_internals/objectClass'; 8 | 9 | // utils 10 | import { 11 | getObjectClass, 12 | parse, 13 | throwUnsupportedError, 14 | } from './_internals/utils'; 15 | 16 | /** 17 | * convert object to a map with different mappings 18 | * based on object type 19 | * 20 | * @param {any} object 21 | * @returns {map} 22 | */ 23 | const toMap = (object) => { 24 | if (typeof Map === 'undefined') { 25 | throwUnsupportedError('Map'); 26 | } 27 | 28 | const objectClass = getObjectClass(object); 29 | 30 | if (objectClass === MAP) { 31 | return object; 32 | } 33 | 34 | if (objectClass === ARRAY) { 35 | return object.reduce((map, value, index) => { 36 | map.set(index, value); 37 | 38 | return map; 39 | }, new Map()); 40 | } 41 | 42 | if (objectClass === OBJECT) { 43 | const map = new Map(); 44 | 45 | let mapKey; 46 | 47 | for (let key in object) { 48 | mapKey = parse(key, () => key); 49 | 50 | map.set(mapKey, object[key]); 51 | } 52 | 53 | return map; 54 | } 55 | 56 | if (objectClass === SET) { 57 | if (typeof Set === 'undefined') { 58 | throwUnsupportedError('Set'); 59 | } 60 | 61 | const map = new Map(); 62 | 63 | let index = 0; 64 | 65 | object.forEach((value) => map.set(index++, value)); 66 | 67 | return map; 68 | } 69 | 70 | const map = new Map(); 71 | 72 | map.set(0, object); 73 | 74 | return map; 75 | }; 76 | 77 | export default toMap; 78 | -------------------------------------------------------------------------------- /src/_internals/numbers.js: -------------------------------------------------------------------------------- 1 | const STOP_POINTS = [ 2 | ['special'], 3 | ['ten'], 4 | ['unit'], 5 | ['ten', 'unit'], 6 | ['unit', 'hundred'], 7 | ['unit', 'hundred', 'special'], 8 | ['unit', 'hundred', 'ten'], 9 | ['unit', 'hundred', 'unit'], 10 | ['unit', 'hundred', 'ten', 'unit'], 11 | ]; 12 | 13 | const TOKENS = { 14 | hundred: ['hundred'], 15 | power: ['thousand', 'million', 'billion', 'trillion'], 16 | special: [ 17 | 'ten', 18 | 'eleven', 19 | 'twelve', 20 | 'thirteen', 21 | 'fourteen', 22 | 'fifteen', 23 | 'sixteen', 24 | 'seventeen', 25 | 'eighteen', 26 | 'nineteen', 27 | ], 28 | ten: ['twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'], 29 | unit: ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'], 30 | }; 31 | 32 | const VALUES = { 33 | billion: 1000000000, 34 | eight: 8, 35 | eighteen: 18, 36 | eighty: 80, 37 | eleven: 11, 38 | false: 0, 39 | fifteen: 15, 40 | fifty: 50, 41 | five: 5, 42 | forty: 40, 43 | four: 4, 44 | fourteen: 14, 45 | hundred: 100, 46 | million: 1000000, 47 | nine: 9, 48 | nineteen: 19, 49 | ninety: 90, 50 | one: 1, 51 | seven: 7, 52 | seventeen: 17, 53 | seventy: 70, 54 | six: 6, 55 | sixteen: 16, 56 | sixty: 60, 57 | ten: 10, 58 | thirteen: 13, 59 | thirty: 30, 60 | thousand: 1000, 61 | three: 3, 62 | trillion: 1000000000000, 63 | true: 1, 64 | twelve: 12, 65 | twenty: 20, 66 | two: 2, 67 | zero: 0, 68 | }; 69 | 70 | export {STOP_POINTS}; 71 | export {TOKENS}; 72 | export {VALUES}; 73 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import toArray from './toArray'; 2 | import toBoolean from './toBoolean'; 3 | import toDate from './toDate'; 4 | import toError from './toError'; 5 | import toFunction from './toFunction'; 6 | import toGenerator from './toGenerator'; 7 | import toMap from './toMap'; 8 | import toNull from './toNull'; 9 | import toNumber from './toNumber'; 10 | import toObject from './toObject'; 11 | import toPromise from './toPromise'; 12 | import toRegExp from './toRegExp'; 13 | import toSet from './toSet'; 14 | import toString from './toString'; 15 | import toSymbol from './toSymbol'; 16 | import toUndefined from './toUndefined'; 17 | 18 | const CONVERTIFY_MAP = { 19 | array: toArray, 20 | boolean: toBoolean, 21 | date: toDate, 22 | error: toError, 23 | function: toFunction, 24 | generator: toGenerator, 25 | map: toMap, 26 | null: toNull, 27 | number: toNumber, 28 | object: toObject, 29 | promise: toPromise, 30 | regexp: toRegExp, 31 | set: toSet, 32 | string: toString, 33 | symbol: toSymbol, 34 | undefined: toUndefined, 35 | }; 36 | 37 | const convertify = (type, object) => { 38 | const normalizedType = typeof type === 'string' ? type.toLowerCase() : ''; 39 | const handler = CONVERTIFY_MAP[normalizedType]; 40 | 41 | if (handler) { 42 | return handler(object); 43 | } 44 | 45 | throw new TypeError('This type of conversion is not currently supported.'); 46 | }; 47 | 48 | for (let key in CONVERTIFY_MAP) { 49 | convertify[key] = CONVERTIFY_MAP[key]; 50 | } 51 | 52 | export { 53 | toArray, 54 | toBoolean, 55 | toDate, 56 | toError, 57 | toFunction, 58 | toGenerator, 59 | toMap, 60 | toNull, 61 | toNumber, 62 | toObject, 63 | toPromise, 64 | toRegExp, 65 | toSet, 66 | toString, 67 | toSymbol, 68 | toUndefined, 69 | }; 70 | 71 | export default convertify; 72 | -------------------------------------------------------------------------------- /test/toSet.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toSet from '../src/toSet'; 4 | 5 | import * as dummyData from './helpers/dummyData'; 6 | 7 | const testIfSetIsEqual = (t, set1, set2) => { 8 | t.deepEqual(set1.values(), set2.values()); 9 | }; 10 | 11 | test('if passing a map returns the same object', (t) => { 12 | t.is(toSet(dummyData.SET), dummyData.SET); 13 | t.is(toSet(dummyData.EMPTY_SET), dummyData.EMPTY_SET); 14 | }); 15 | 16 | const STRINGS_TO_NOT_TEST_AUTOMATEDLY = [ 17 | 'ARRAY', 18 | 'EMPTY_ARRAY', 19 | 'EMPTY_OBJECT', 20 | 'EMPTY_MAP', 21 | 'EMPTY_SET', 22 | 'MAP', 23 | 'OBJECT', 24 | 'SET', 25 | ]; 26 | 27 | test('if passing simply-mapped values yield the original object wrapped in an array', (t) => { 28 | const resultData = Object.assign({}, dummyData); 29 | 30 | for (let key in resultData) { 31 | if (!~STRINGS_TO_NOT_TEST_AUTOMATEDLY.indexOf(key)) { 32 | testIfSetIsEqual(t, toSet(resultData[key]), new Set([resultData[key]])); 33 | } 34 | } 35 | }); 36 | 37 | test('if passing mapped values yield the correct result', (t) => { 38 | let objectToArray = [], 39 | mapToArray = []; 40 | 41 | for (let key in dummyData.OBJECT) { 42 | objectToArray.push(dummyData.OBJECT[key]); 43 | } 44 | 45 | dummyData.MAP.forEach((value) => { 46 | mapToArray.push(value); 47 | }); 48 | 49 | testIfSetIsEqual(t, toSet(dummyData.ARRAY), new Set(dummyData.ARRAY)); 50 | testIfSetIsEqual(t, toSet(dummyData.EMPTY_ARRAY), new Set()); 51 | testIfSetIsEqual(t, toSet(dummyData.EMPTY_OBJECT), new Set()); 52 | testIfSetIsEqual(t, toSet(dummyData.EMPTY_MAP), new Set()); 53 | testIfSetIsEqual(t, toSet(dummyData.EMPTY_SET), new Set()); 54 | testIfSetIsEqual(t, toSet(dummyData.MAP), new Set(mapToArray)); 55 | testIfSetIsEqual(t, toSet(dummyData.OBJECT), new Set(objectToArray)); 56 | }); 57 | -------------------------------------------------------------------------------- /src/toObject.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | ARRAY, 4 | OBJECT, 5 | MAP, 6 | SET, 7 | STRING, 8 | } from './_internals/objectClass'; 9 | 10 | // transform 11 | import toString from './toString'; 12 | 13 | // utils 14 | import { 15 | getObjectClass, 16 | isEncapsulatedBy, 17 | } from './_internals/utils'; 18 | 19 | /** 20 | * for most values, return a new object with a single key-value 21 | * pair, where the object passed is the value 22 | * 23 | * @param {any} object 24 | * @returns {{0: any}} 25 | */ 26 | const returnDefaultObject = (object) => ({ 27 | 0: object, 28 | }); 29 | 30 | /** 31 | * convert object to proper object with different mappings 32 | * based on object type 33 | * 34 | * @param {any} object 35 | * @returns {object} 36 | */ 37 | const toObject = (object) => { 38 | const objectClass = getObjectClass(object); 39 | 40 | if (objectClass === OBJECT) { 41 | return object; 42 | } 43 | 44 | if (objectClass === ARRAY) { 45 | return object.reduce((newObject, value, index) => { 46 | newObject[index] = value; 47 | 48 | return newObject; 49 | }, {}); 50 | } 51 | 52 | if (objectClass === MAP) { 53 | const newObject = {}; 54 | 55 | object.forEach((value, key) => { 56 | newObject[getObjectClass(key) === STRING ? key : toString(key)] = value; 57 | }); 58 | 59 | return newObject; 60 | } 61 | 62 | if (objectClass === SET) { 63 | const newObject = {}; 64 | 65 | let index = 0; 66 | 67 | object.forEach((value) => { 68 | newObject[index++] = value; 69 | }); 70 | 71 | return newObject; 72 | } 73 | 74 | if (objectClass === STRING) { 75 | if (isEncapsulatedBy(object, '{', '}')) { 76 | try { 77 | return JSON.parse(object); 78 | } catch (exception) { 79 | return returnDefaultObject(object); 80 | } 81 | } 82 | 83 | return returnDefaultObject(object); 84 | } 85 | 86 | switch (getObjectClass(object)) { 87 | case STRING: 88 | 89 | default: 90 | return returnDefaultObject(object); 91 | } 92 | }; 93 | 94 | export default toObject; 95 | -------------------------------------------------------------------------------- /test/toArray.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toArray from '../src/toArray'; 4 | 5 | import { 6 | ARRAY, 7 | BOOLEAN, 8 | DATE, 9 | EMPTY_ARRAY, 10 | EMPTY_MAP, 11 | EMPTY_OBJECT, 12 | EMPTY_SET, 13 | EMPTY_STRING, 14 | ERROR, 15 | FUNCTION, 16 | GENERATOR, 17 | MAP, 18 | NULL, 19 | NUMBER, 20 | OBJECT, 21 | PROMISE, 22 | REGEXP, 23 | SET, 24 | STRING, 25 | SYMBOL, 26 | UNDEFINED, 27 | } from './helpers/dummyData'; 28 | 29 | test('if passing an array returns the same object', (t) => { 30 | t.is(toArray(ARRAY), ARRAY); 31 | t.is(toArray(EMPTY_ARRAY), EMPTY_ARRAY); 32 | }); 33 | 34 | test('if passing unmapped values yield the original object wrapped in an array', (t) => { 35 | t.deepEqual(toArray(BOOLEAN), [BOOLEAN]); 36 | t.deepEqual(toArray(DATE), [DATE]); 37 | t.deepEqual(toArray(ERROR), [ERROR]); 38 | t.deepEqual(toArray(EMPTY_STRING), [EMPTY_STRING]); 39 | t.deepEqual(toArray(FUNCTION), [FUNCTION]); 40 | t.deepEqual(toArray(GENERATOR), [GENERATOR]); 41 | t.deepEqual(toArray(NULL), [NULL]); 42 | t.deepEqual(toArray(NUMBER), [NUMBER]); 43 | t.deepEqual(toArray(PROMISE), [PROMISE]); 44 | t.deepEqual(toArray(REGEXP), [REGEXP]); 45 | t.deepEqual(toArray(STRING), [STRING]); 46 | t.deepEqual(toArray(SYMBOL), [SYMBOL]); 47 | t.deepEqual(toArray(UNDEFINED), [UNDEFINED]); 48 | }); 49 | 50 | test('if passing mapped values yield the correct result', (t) => { 51 | let mapToArray = [], 52 | objectToArray = [], 53 | setToArray = []; 54 | 55 | MAP.forEach((value) => { 56 | mapToArray.push(value); 57 | }); 58 | 59 | for (let key in OBJECT) { 60 | objectToArray.push(OBJECT[key]); 61 | } 62 | 63 | SET.forEach((value) => { 64 | setToArray.push(value); 65 | }); 66 | 67 | t.deepEqual(toArray(EMPTY_OBJECT), []); 68 | t.deepEqual(toArray(EMPTY_MAP), []); 69 | t.deepEqual(toArray(EMPTY_SET), []); 70 | t.deepEqual(toArray(MAP), mapToArray); 71 | t.deepEqual(toArray(OBJECT), objectToArray); 72 | t.deepEqual(toArray(SET), setToArray); 73 | 74 | t.deepEqual(toArray(JSON.stringify(ARRAY)), ARRAY); 75 | }); 76 | -------------------------------------------------------------------------------- /src/_internals/objectClass.js: -------------------------------------------------------------------------------- 1 | export const ARGUMENTS = '[object Arguments]'; 2 | export const ARRAY = '[object Array]'; 3 | export const ARRAY_BUFFER = '[object ArrayBuffer]'; 4 | export const BOOLEAN = '[object Boolean]'; 5 | export const DATA_VIEW = '[object DataView]'; 6 | export const DATE = '[object Date]'; 7 | export const ERROR = '[object Error]'; 8 | export const FLOAT_32_ARRAY = '[object Float32Array]'; 9 | export const FLOAT_64_ARRAY = '[object Float64Array]'; 10 | export const FUNCTION = '[object Function]'; 11 | export const GENERATOR = '[object GeneratorFunction]'; 12 | export const INT_8_ARRAY = '[object Int8Array]'; 13 | export const INT_16_ARRAY = '[object Int16Array]'; 14 | export const INT_32_ARRAY = '[object Int32Array]'; 15 | export const MAP = '[object Map]'; 16 | export const MATH = '[object Math]'; 17 | export const NULL = '[object Null]'; 18 | export const NUMBER = '[object Number]'; 19 | export const OBJECT = '[object Object]'; 20 | export const PROMISE = '[object Promise]'; 21 | export const REGEXP = '[object RegExp]'; 22 | export const SET = '[object Set]'; 23 | export const STRING = '[object String]'; 24 | export const SYMBOL = '[object Symbol]'; 25 | export const UINT_8_ARRAY = '[object Uint8Array]'; 26 | export const UINT_8_CLAMPED_ARRAY = '[object Uint8ClampedArray]'; 27 | export const UINT_16_ARRAY = '[object Uint16Array]'; 28 | export const UINT_32_ARRAY = '[object Uint32Array]'; 29 | export const UNDEFINED = '[object Undefined]'; 30 | export const WEAKMAP = '[object WeakMap]'; 31 | export const WEAKSET = '[object WeakSet]'; 32 | export const WINDOW = '[object Window]'; 33 | 34 | export default { 35 | ARGUMENTS, 36 | ARRAY, 37 | ARRAY_BUFFER, 38 | BOOLEAN, 39 | DATA_VIEW, 40 | DATE, 41 | ERROR, 42 | FLOAT_32_ARRAY, 43 | FLOAT_64_ARRAY, 44 | FUNCTION, 45 | GENERATOR, 46 | INT_8_ARRAY, 47 | INT_16_ARRAY, 48 | INT_32_ARRAY, 49 | MAP, 50 | MATH, 51 | NULL, 52 | NUMBER, 53 | OBJECT, 54 | PROMISE, 55 | REGEXP, 56 | SET, 57 | STRING, 58 | SYMBOL, 59 | UINT_8_ARRAY, 60 | UINT_8_CLAMPED_ARRAY, 61 | UINT_16_ARRAY, 62 | UINT_32_ARRAY, 63 | UNDEFINED, 64 | WEAKMAP, 65 | WEAKSET, 66 | WINDOW, 67 | }; 68 | -------------------------------------------------------------------------------- /test/toObject.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toObject from '../src/toObject'; 4 | 5 | import { 6 | ARRAY, 7 | BOOLEAN, 8 | DATE, 9 | EMPTY_ARRAY, 10 | EMPTY_MAP, 11 | EMPTY_OBJECT, 12 | EMPTY_SET, 13 | EMPTY_STRING, 14 | ERROR, 15 | FUNCTION, 16 | GENERATOR, 17 | MAP, 18 | NULL, 19 | NUMBER, 20 | OBJECT, 21 | PROMISE, 22 | REGEXP, 23 | SET, 24 | STRING, 25 | SYMBOL, 26 | UNDEFINED 27 | } from './helpers/dummyData'; 28 | 29 | test('if passing an object returns the same object', (t) => { 30 | t.is(toObject(OBJECT), OBJECT); 31 | t.is(toObject(EMPTY_OBJECT), EMPTY_OBJECT); 32 | }); 33 | 34 | test('if passing simply-mapped values yield the correct result', (t) => { 35 | t.deepEqual(toObject(BOOLEAN), {'0': BOOLEAN}); 36 | t.deepEqual(toObject(DATE), {'0': DATE}); 37 | t.deepEqual(toObject(ERROR), {'0': ERROR}); 38 | t.deepEqual(toObject(EMPTY_STRING), {'0': EMPTY_STRING}); 39 | t.deepEqual(toObject(FUNCTION), {'0': FUNCTION}); 40 | t.deepEqual(toObject(GENERATOR), {'0': GENERATOR}); 41 | t.deepEqual(toObject(NULL), {'0': NULL}); 42 | t.deepEqual(toObject(NUMBER), {'0': NUMBER}); 43 | t.deepEqual(toObject(PROMISE), {'0': PROMISE}); 44 | t.deepEqual(toObject(REGEXP), {'0': REGEXP}); 45 | t.deepEqual(toObject(STRING), {'0': STRING}); 46 | t.deepEqual(toObject(SYMBOL), {'0': SYMBOL}); 47 | t.deepEqual(toObject(UNDEFINED), {'0': UNDEFINED}); 48 | }); 49 | 50 | test('if passing mapped values yield the original object wrapped in an array', (t) => { 51 | const arrayToObject = ARRAY.reduce((object, value, index) => { 52 | return { 53 | ...object, 54 | [index]: value 55 | }; 56 | }, {}); 57 | 58 | let mapToObject = {}, 59 | setToObject = {}; 60 | 61 | MAP.forEach((value, key) => { 62 | mapToObject[key] = value; 63 | }); 64 | 65 | let index = 0; 66 | 67 | SET.forEach((value) => { 68 | setToObject[index] = value; 69 | 70 | index++; 71 | }); 72 | 73 | const objectKeyMap = new Map().set({foo: 'bar'}, 'baz'); 74 | const stringifiedKeyObject = { 75 | '{"foo":"bar"}': 'baz' 76 | }; 77 | 78 | t.deepEqual(toObject(ARRAY), arrayToObject); 79 | t.deepEqual(toObject(EMPTY_ARRAY), {}); 80 | t.deepEqual(toObject(EMPTY_MAP), {}); 81 | t.deepEqual(toObject(EMPTY_SET), {}); 82 | t.deepEqual(toObject(MAP), mapToObject); 83 | t.deepEqual(toObject(SET), setToObject); 84 | 85 | t.deepEqual(toObject(objectKeyMap), stringifiedKeyObject); 86 | t.deepEqual(toObject(JSON.stringify(OBJECT)), OBJECT); 87 | }); -------------------------------------------------------------------------------- /src/toGenerator.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | ARRAY, 4 | GENERATOR, 5 | MAP, 6 | OBJECT, 7 | SET, 8 | } from './_internals/objectClass'; 9 | 10 | // utils 11 | import { 12 | getObjectClass, 13 | throwUnsupportedError, 14 | } from './_internals/utils'; 15 | 16 | const throwErrorIfUnsupported = () => { 17 | try { 18 | /* eslint-disable no-eval */ 19 | eval('(function *(){})'); 20 | /* eslint-enable */ 21 | } catch (exception) { 22 | throwUnsupportedError('Generator'); 23 | } 24 | }; 25 | 26 | /** 27 | * convert object to generator function with returned 28 | * generators based on the object type 29 | * 30 | * @param {any} object 31 | * @returns {generator} 32 | */ 33 | const toGenerator = (object) => { 34 | throwErrorIfUnsupported(); 35 | 36 | const objectClass = getObjectClass(object); 37 | 38 | if (objectClass === GENERATOR) { 39 | return object; 40 | } 41 | 42 | if (objectClass === ARRAY) { 43 | return function* () { 44 | const length = object.length; 45 | 46 | let index = -1; 47 | 48 | while (++index < length) { 49 | yield object[index]; 50 | } 51 | }; 52 | } 53 | 54 | if (objectClass === MAP) { 55 | return function* () { 56 | let keys = [], 57 | values = {}; 58 | 59 | object.forEach((value, key) => { 60 | keys.push(key); 61 | 62 | values[key] = value; 63 | }); 64 | 65 | const length = object.size; 66 | 67 | let index = -1, 68 | key; 69 | 70 | while (++index < length) { 71 | key = keys[index]; 72 | 73 | yield values[key]; 74 | } 75 | }; 76 | } 77 | 78 | if (objectClass === OBJECT) { 79 | return function* () { 80 | const keys = Object.keys(object); 81 | const length = keys.length; 82 | 83 | let index = -1, 84 | key; 85 | 86 | while (++index < length) { 87 | key = keys[index]; 88 | 89 | yield object[key]; 90 | } 91 | }; 92 | } 93 | 94 | if (objectClass === SET) { 95 | return function* () { 96 | let values = []; 97 | 98 | object.forEach((value) => { 99 | values.push(value); 100 | }); 101 | 102 | const length = object.size; 103 | 104 | let index = -1; 105 | 106 | while (++index < length) { 107 | yield values[index]; 108 | } 109 | }; 110 | } 111 | 112 | return function* () { 113 | yield object; 114 | }; 115 | }; 116 | 117 | export default toGenerator; 118 | -------------------------------------------------------------------------------- /src/_internals/utils.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import fastStringify from 'fast-stringify'; 3 | 4 | /** 5 | * @function circularStringify 6 | * 7 | * @description 8 | * use fast-stringify to handle circular objects 9 | * 10 | * @param {*} object the object to stringify 11 | * @param {function} replacer the custom replacer 12 | * @param {function} onFail the method to call when a failure occurs 13 | * @returns {string} the stringified value 14 | */ 15 | export const circularStringify = (object, replacer, onFail) => { 16 | try { 17 | return fastStringify(object, replacer); 18 | } catch (nonCircularError) { 19 | return onFail(object); 20 | } 21 | }; 22 | 23 | /** 24 | * get the toString value of object 25 | * 26 | * @param {any} object 27 | * @returns {string} 28 | */ 29 | export const getObjectClass = (object) => toString.call(object); 30 | 31 | /** 32 | * determine if string starts with begin and ends with end 33 | * 34 | * @param {string} string 35 | * @param {string} begin 36 | * @param {string} end 37 | * @returns {boolean} 38 | */ 39 | export const isEncapsulatedBy = (string, begin, end = begin) => 40 | typeof string === 'string' && string.indexOf(begin) === 0 && string.lastIndexOf(end) === string.length - 1; 41 | 42 | /** 43 | * @function parse 44 | * 45 | * @description 46 | * parse the string into a valid JS object 47 | * 48 | * @param {string} string the string to parse 49 | * @param {function} onFail the method to call when a failure occurs 50 | * @returns {*} the parsed string 51 | */ 52 | export const parse = (string, onFail) => { 53 | try { 54 | return JSON.parse(string); 55 | } catch (error) { 56 | return onFail(string); 57 | } 58 | }; 59 | 60 | /** 61 | * @function stringify 62 | * 63 | * @description 64 | * stringify the object 65 | * 66 | * @param {*} object the object to stringify 67 | * @param {function} replacer the custom replacer method 68 | * @returns {string} the stringified object 69 | */ 70 | export const stringify = (object, replacer) => { 71 | try { 72 | return JSON.stringify(object, replacer); 73 | } catch (error) { 74 | return circularStringify(object, replacer); 75 | } 76 | }; 77 | 78 | /** 79 | * throw a new error saying the specific object class is not supported 80 | * 81 | * @param {string} objectClass 82 | */ 83 | export const throwUnsupportedError = (objectClass) => { 84 | throw new Error( 85 | `${objectClass} 'is not supported in your current browser. If you are using babel, make sure to import babel-polyfill into your project, otherwise you should add your own polyfill, such as core-js.'` 86 | ); 87 | }; 88 | -------------------------------------------------------------------------------- /src/toDate.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | ARRAY, 4 | BOOLEAN, 5 | DATE, 6 | MAP, 7 | OBJECT, 8 | SET, 9 | STRING, 10 | SYMBOL, 11 | } from './_internals/objectClass'; 12 | 13 | // utils 14 | import {getObjectClass} from './_internals/utils'; 15 | 16 | const {keys} = Object; 17 | const {hasOwnProperty} = Object.prototype; 18 | 19 | const OBJECT_TO_DATE_ORDER = ['year', 'month', 'day', 'hour', 'minutes', 'seconds', 'milliseconds']; 20 | const OBJECT_TO_DATE_ORDER_LENGTH = OBJECT_TO_DATE_ORDER.length; 21 | 22 | const getInvalidDate = () => new Date('invalid'); 23 | 24 | /** 25 | * convert object to date with different mappings 26 | * based on object type 27 | * 28 | * @param {any} object 29 | * @returns {date} 30 | */ 31 | const toDate = (object) => { 32 | // eslint-disable-next-line eqeqeq 33 | if (object == null) { 34 | return new Date(); 35 | } 36 | 37 | const objectClass = getObjectClass(object); 38 | 39 | if (objectClass === DATE) { 40 | return object; 41 | } 42 | 43 | if (objectClass === ARRAY) { 44 | return new Date(...object); 45 | } 46 | 47 | if (objectClass === OBJECT) { 48 | if (!keys(object).length) { 49 | return new Date(); 50 | } 51 | 52 | const dateArray = []; 53 | 54 | let index = -1; 55 | let key; 56 | 57 | while (++index < OBJECT_TO_DATE_ORDER_LENGTH) { 58 | key = OBJECT_TO_DATE_ORDER[index]; 59 | 60 | if (hasOwnProperty.call(object, key)) { 61 | dateArray.push(object[key]); 62 | } 63 | } 64 | 65 | return dateArray.length ? new Date(...dateArray) : getInvalidDate(); 66 | } 67 | 68 | if (objectClass === MAP) { 69 | if (!object.size) { 70 | return new Date(); 71 | } 72 | 73 | const dateArray = []; 74 | 75 | let index = -1; 76 | let key; 77 | 78 | while (++index < OBJECT_TO_DATE_ORDER_LENGTH) { 79 | key = OBJECT_TO_DATE_ORDER[index]; 80 | 81 | if (object.has(key)) { 82 | dateArray.push(object.get(key)); 83 | } 84 | } 85 | 86 | return dateArray.length ? new Date(...dateArray) : getInvalidDate(); 87 | } 88 | 89 | if (objectClass === SET) { 90 | if (!object.size) { 91 | return new Date(); 92 | } 93 | 94 | const dateArray = []; 95 | 96 | object.forEach((value) => dateArray.push(value)); 97 | 98 | return new Date(...dateArray); 99 | } 100 | 101 | if (objectClass === BOOLEAN || objectClass === SYMBOL) { 102 | return getInvalidDate(); 103 | } 104 | 105 | if (objectClass === STRING) { 106 | return object ? new Date(object) : new Date(); 107 | } 108 | 109 | return new Date(object); 110 | }; 111 | 112 | export default toDate; 113 | -------------------------------------------------------------------------------- /test/toMap.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toMap from '../src/toMap'; 4 | 5 | import { 6 | ARRAY, 7 | BOOLEAN, 8 | DATE, 9 | EMPTY_ARRAY, 10 | EMPTY_MAP, 11 | EMPTY_OBJECT, 12 | EMPTY_SET, 13 | EMPTY_STRING, 14 | ERROR, 15 | FUNCTION, 16 | GENERATOR, 17 | MAP, 18 | NULL, 19 | NUMBER, 20 | OBJECT, 21 | PROMISE, 22 | REGEXP, 23 | SET, 24 | STRING, 25 | SYMBOL, 26 | UNDEFINED 27 | } from './helpers/dummyData'; 28 | 29 | const testIfMapIsEqual = (t, map1, map2) => { 30 | t.deepEqual(map1.keys(), map2.keys()); 31 | t.deepEqual(map1.values(), map2.values()); 32 | }; 33 | 34 | test('if passing a map returns the same object', (t) => { 35 | t.is(toMap(MAP), MAP); 36 | t.is(toMap(EMPTY_MAP), EMPTY_MAP); 37 | }); 38 | 39 | test('if passing simply-mapped values yield the original object wrapped in an array', (t) => { 40 | testIfMapIsEqual(t, toMap(BOOLEAN), new Map([[0, BOOLEAN]])); 41 | testIfMapIsEqual(t, toMap(DATE), new Map([[0, DATE]])); 42 | testIfMapIsEqual(t, toMap(ERROR), new Map([[0, ERROR]])); 43 | testIfMapIsEqual(t, toMap(EMPTY_STRING), new Map([[0, EMPTY_STRING]])); 44 | testIfMapIsEqual(t, toMap(FUNCTION), new Map([[0, FUNCTION]])); 45 | testIfMapIsEqual(t, toMap(GENERATOR), new Map([[0, GENERATOR]])); 46 | testIfMapIsEqual(t, toMap(NULL), new Map([[0, NULL]])); 47 | testIfMapIsEqual(t, toMap(NUMBER), new Map([[0, NUMBER]])); 48 | testIfMapIsEqual(t, toMap(PROMISE), new Map([[0, PROMISE]])); 49 | testIfMapIsEqual(t, toMap(REGEXP), new Map([[0, REGEXP]])); 50 | testIfMapIsEqual(t, toMap(STRING), new Map([[0, STRING]])); 51 | testIfMapIsEqual(t, toMap(SYMBOL), new Map([[0, SYMBOL]])); 52 | testIfMapIsEqual(t, toMap(UNDEFINED), new Map([[0, UNDEFINED]])); 53 | }); 54 | 55 | test('if passing mapped values yield the correct result', (t) => { 56 | let objectToMap = [], 57 | setToMap = [], 58 | index = 0; 59 | 60 | const mappedArray = ARRAY.map((value, index) => { 61 | return [index, value]; 62 | }); 63 | 64 | for (let key in OBJECT) { 65 | objectToMap.push([key, OBJECT[key]]) 66 | } 67 | 68 | SET.forEach((value) => { 69 | setToMap.push([index, value]); 70 | 71 | index++; 72 | }); 73 | 74 | const objectWithStringifiedKey = { 75 | [JSON.stringify({foo: 'bar'})]: 'baz' 76 | }; 77 | 78 | testIfMapIsEqual(t, toMap(ARRAY), new Map(mappedArray)); 79 | testIfMapIsEqual(t, toMap(EMPTY_ARRAY), new Map()); 80 | testIfMapIsEqual(t, toMap(EMPTY_OBJECT), new Map()); 81 | testIfMapIsEqual(t, toMap(EMPTY_MAP), new Map()); 82 | testIfMapIsEqual(t, toMap(EMPTY_SET), new Map()); 83 | testIfMapIsEqual(t, toMap(OBJECT), new Map(objectToMap)); 84 | testIfMapIsEqual(t, toMap(SET), new Map(setToMap)); 85 | testIfMapIsEqual(t, toMap(objectWithStringifiedKey), new Map().set({foo: 'bar'}, 'baz')); 86 | }); -------------------------------------------------------------------------------- /test/toDate.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toDate from '../src/toDate'; 4 | 5 | import { 6 | ARRAY, 7 | BOOLEAN, 8 | DATE, 9 | EMPTY_ARRAY, 10 | EMPTY_MAP, 11 | EMPTY_OBJECT, 12 | EMPTY_SET, 13 | EMPTY_STRING, 14 | ERROR, 15 | FUNCTION, 16 | GENERATOR, 17 | MAP, 18 | NULL, 19 | NUMBER, 20 | OBJECT, 21 | PROMISE, 22 | REGEXP, 23 | SET, 24 | STRING, 25 | SYMBOL, 26 | UNDEFINED, 27 | } from './helpers/dummyData'; 28 | 29 | const testIsDateMostlyEqual = (date1, date2) => { 30 | const date1String = date1 31 | .toISOString() 32 | .split(':') 33 | .slice(0, -1) 34 | .join(':'); 35 | const date2String = date2 36 | .toISOString() 37 | .split(':') 38 | .slice(0, -1) 39 | .join(':'); 40 | 41 | return date1String === date2String; 42 | }; 43 | 44 | const VALID_DATE_ARRAY = [2000, 0, 1]; 45 | const VALID_DATE_MAP = new Map() 46 | .set('day', 1) 47 | .set('month', 0) 48 | .set('year', 2000); 49 | const VALID_DATE_OBJECT = { 50 | day: 1, 51 | month: 0, 52 | year: 2000, 53 | }; 54 | const VALID_DATE_SET = new Set([2000, 0, 1]); 55 | const VALID_DATE_RESULT = new Date(2000, 0, 1); 56 | 57 | test('if passing a date returns the same object', (t) => { 58 | t.is(toDate(DATE), DATE); 59 | }); 60 | 61 | test('if passing invalid date values are identified as invalid dates', (t) => { 62 | t.true(isNaN(toDate(ARRAY).valueOf())); 63 | t.true(isNaN(toDate(BOOLEAN).valueOf())); 64 | t.true(isNaN(toDate(ERROR).valueOf())); 65 | t.true(isNaN(toDate(FUNCTION).valueOf())); 66 | t.true(isNaN(toDate(GENERATOR).valueOf())); 67 | t.true(isNaN(toDate(MAP).valueOf())); 68 | t.true(isNaN(toDate(OBJECT).valueOf())); 69 | t.true(isNaN(toDate(PROMISE).valueOf())); 70 | t.true(isNaN(toDate(REGEXP).valueOf())); 71 | t.true(isNaN(toDate(SET).valueOf())); 72 | t.true(isNaN(toDate(STRING).valueOf())); 73 | t.true(isNaN(toDate(SYMBOL).valueOf())); 74 | }); 75 | 76 | test('if passing values mapped to new dates yield new dates', (t) => { 77 | t.true(testIsDateMostlyEqual(toDate(EMPTY_ARRAY), new Date())); 78 | t.true(testIsDateMostlyEqual(toDate(EMPTY_MAP), new Date())); 79 | t.true(testIsDateMostlyEqual(toDate(EMPTY_OBJECT), new Date())); 80 | t.true(testIsDateMostlyEqual(toDate(EMPTY_SET), new Date())); 81 | t.true(testIsDateMostlyEqual(toDate(EMPTY_STRING), new Date())); 82 | t.true(testIsDateMostlyEqual(toDate(NULL), new Date())); 83 | t.true(testIsDateMostlyEqual(toDate(UNDEFINED), new Date())); 84 | }); 85 | 86 | test('if passing standard date input values yield the correct date', (t) => { 87 | t.deepEqual(toDate(NUMBER), new Date('1970-01-01T00:00:00.123Z')); 88 | t.deepEqual(toDate(VALID_DATE_ARRAY), VALID_DATE_RESULT); 89 | t.deepEqual(toDate(VALID_DATE_MAP), VALID_DATE_RESULT); 90 | t.deepEqual(toDate(VALID_DATE_OBJECT), VALID_DATE_RESULT); 91 | t.deepEqual(toDate(VALID_DATE_SET), VALID_DATE_RESULT); 92 | }); 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "planttheidea", 3 | "ava": { 4 | "babel": "inherit", 5 | "failFast": true, 6 | "files": [ 7 | "test/*.js" 8 | ], 9 | "require": [ 10 | "babel-register", 11 | "babel-polyfill", 12 | "./test/helpers/setup-browser-env.js" 13 | ], 14 | "verbose": true 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/planttheidea/convertify/issues" 18 | }, 19 | "dependencies": { 20 | "fast-stringify": "^1.1.1" 21 | }, 22 | "description": "A simple convesion tool from one object type to the next", 23 | "devDependencies": { 24 | "ava": "^0.25.0", 25 | "babel-cli": "6.26.0", 26 | "babel-core": "^6.26.3", 27 | "babel-eslint": "10.0.1", 28 | "babel-loader": "^7.1.5", 29 | "babel-plugin-add-module-exports": "1.0.0", 30 | "babel-polyfill": "^6.26.0", 31 | "babel-preset-env": "^1.7.0", 32 | "babel-preset-react": "6.24.1", 33 | "babel-preset-stage-2": "6.24.1", 34 | "babel-register": "^6.11.6", 35 | "browser-env": "^3.2.5", 36 | "eslint": "5.12.0", 37 | "eslint-config-rapid7": "^3.1.0", 38 | "eslint-friendly-formatter": "4.0.1", 39 | "eslint-loader": "2.1.1", 40 | "html-webpack-plugin": "^3.2.0", 41 | "in-publish": "^2.0.0", 42 | "nyc": "^13.1.0", 43 | "react": "^16.7.0", 44 | "react-dom": "^16.7.0", 45 | "rimraf": "^2.6.3", 46 | "sinon": "^7.2.2", 47 | "webpack": "4.28.3", 48 | "webpack-cli": "^3.2.0", 49 | "webpack-dev-server": "3.1.14" 50 | }, 51 | "homepage": "https://github.com/planttheidea/convertify#readme", 52 | "keywords": [ 53 | "convert", 54 | "to", 55 | "number", 56 | "string", 57 | "array", 58 | "object" 59 | ], 60 | "license": "MIT", 61 | "main": "lib/index.js", 62 | "module": "es/index.js", 63 | "name": "convertify", 64 | "repository": { 65 | "type": "git", 66 | "url": "git+https://github.com/planttheidea/convertify.git" 67 | }, 68 | "scripts": { 69 | "build": "NODE_ENV=development webpack --progress --colors", 70 | "build:minified": "NODE_ENV=production webpack --progress --colors --config=webpack.config.minified.js", 71 | "dev": "NODE_ENV=development webpack-dev-server --progress --colors --config=webpack.config.dev.js", 72 | "clean": "rimraf lib && rimraf es && rimraf dist", 73 | "lint": "NODE_ENV=test eslint src", 74 | "lint:fix": "npm run lint -- --fix", 75 | "prepublish": "if in-publish; then npm run prepublish:compile; fi", 76 | "prepublish:compile": "npm run lint && npm run test:coverage && npm run clean && npm run transpile:lib && npm run transpile:es && npm run build && npm run build:minified", 77 | "test": "NODE_ENV=test ava", 78 | "test:coverage": "nyc npm test", 79 | "test:watch": "npm test -- --watch", 80 | "transpile:es": "BABEL_ENV=es babel src --out-dir es", 81 | "transpile:lib": "BABEL_ENV=lib babel src --out-dir lib" 82 | }, 83 | "version": "1.0.4" 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # convertify 2 | 3 | A simple conversion tool from one object type to the next 4 | 5 | #### Installation 6 | 7 | ``` 8 | $ npm i convertify --save 9 | ``` 10 | 11 | #### Usage 12 | 13 | ```javascript 14 | // ES2015 15 | import to from "convertify"; 16 | 17 | // CommonJS 18 | const to = require("convertify"); 19 | 20 | // script 21 | const to = window.convertify; 22 | ``` 23 | 24 | Or you can bring in individual functions: 25 | 26 | ```javascript 27 | // in this case toString, which is the same as to.string 28 | 29 | // ES2015 30 | import { toString } from "convertify"; 31 | 32 | // CommonJS 33 | const toString = require("convertify").toString; 34 | 35 | // script 36 | const toString = window.convertify.toString; 37 | ``` 38 | 39 | #### Implementation 40 | 41 | ```javascript 42 | const someObject = { 43 | foo: "bar" 44 | }; 45 | 46 | // you can use the default method 47 | const map = to("map", someObject); // Map {'foo' => 'bar'} 48 | 49 | // or the convenience methods 50 | const array = to.array(map); // ['bar'] 51 | ``` 52 | 53 | #### API 54 | 55 | Link here: [API](API.md) 56 | 57 | **to(convertTo: string, object: any)** 58 | 59 | The standard method accepts two parameters, the first being a string of the object class you wnat to convert the object to, and the second being the object itself. The valid object classes you can convert to: 60 | 61 | - `array` 62 | - `boolean` 63 | - `date` 64 | - `error` 65 | - `function` 66 | - `generator` 67 | - `map` 68 | - `null` 69 | - `number` 70 | - `object` 71 | - `promise` 72 | - `regexp` 73 | - `set` 74 | - `string` 75 | - `symbol` 76 | - `undefined` 77 | 78 | These values are either the string value you pass in as the first parameter to `to`, or the shorthand method name for that specific conversion: 79 | 80 | ```javascript 81 | const stringifiedObject = to("string", object); 82 | 83 | // is the same as 84 | 85 | const alsoStringifiedObject = to.string(object); 86 | ``` 87 | 88 | The conversions themselves try to be smarter than the standard implicit conversions, so check out the [API](API.md) to learn more details about specific conversion results. 89 | 90 | ### Development 91 | 92 | Standard stuff, clone the repo and `npm i` to get the dependencies. npm scripts available: 93 | 94 | - `build` => builds the distributed JS with `NODE_ENV=development` and with sourcemaps 95 | - `build-minified` => builds the distributed JS with `NODE_ENV=production` and minified 96 | - `compile-for-publish` => runs the `lint`, `test`, `transpile`, `build`, and `build-minified` scripts 97 | - `dev` => runs the webpack dev server for the playground 98 | - `lint` => runs ESLint against files in the `src` folder 99 | - `prepublish` => if in publish, runs `compile-for-publish` 100 | - `test` => run ava with NODE_ENV=test 101 | - `test:watch` => runs `test` but with persistent watcher 102 | - `transpile` => runs Babel against files in `src` to files in `lib` 103 | -------------------------------------------------------------------------------- /test/toNumber.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toNumber from '../src/toNumber'; 4 | 5 | import { 6 | ARRAY, 7 | BOOLEAN, 8 | DATE, 9 | EMPTY_ARRAY, 10 | EMPTY_MAP, 11 | EMPTY_OBJECT, 12 | EMPTY_SET, 13 | EMPTY_STRING, 14 | ERROR, 15 | FUNCTION, 16 | GENERATOR, 17 | MAP, 18 | NULL, 19 | NUMBER, 20 | OBJECT, 21 | PROMISE, 22 | REGEXP, 23 | SET, 24 | STRING, 25 | SYMBOL, 26 | UNDEFINED, 27 | } from './helpers/dummyData'; 28 | 29 | test('if passing number returns the same object', (t) => { 30 | t.is(toNumber(NUMBER), NUMBER); 31 | }); 32 | 33 | test('if numeric strings return the parsed value of the string', (t) => { 34 | t.is(toNumber('1'), parseFloat(1)); 35 | t.is(toNumber('0.234'), parseFloat(0.234)); 36 | t.is(toNumber('12345'), parseFloat(12345)); 37 | }); 38 | 39 | test('if simple string units return their numeric counterparts', (t) => { 40 | t.is(toNumber('one'), 1); 41 | t.is(toNumber('two'), 2); 42 | t.is(toNumber('three'), 3); 43 | t.is(toNumber('four'), 4); 44 | t.is(toNumber('five'), 5); 45 | t.is(toNumber('six'), 6); 46 | t.is(toNumber('seven'), 7); 47 | t.is(toNumber('eight'), 8); 48 | t.is(toNumber('nine'), 9); 49 | t.is(toNumber('ten'), 10); 50 | t.is(toNumber('eleven'), 11); 51 | t.is(toNumber('twelve'), 12); 52 | t.is(toNumber('thirteen'), 13); 53 | t.is(toNumber('fourteen'), 14); 54 | t.is(toNumber('fifteen'), 15); 55 | t.is(toNumber('sixteen'), 16); 56 | t.is(toNumber('seventeen'), 17); 57 | t.is(toNumber('eighteen'), 18); 58 | t.is(toNumber('nineteen'), 19); 59 | }); 60 | 61 | test('if string units with powers return their numeric counterparts', (t) => { 62 | t.is(toNumber('one hundred'), 100); 63 | t.is(toNumber('one thousand'), 1000); 64 | t.is(toNumber('one million'), 1000000); 65 | t.is(toNumber('one billion'), 1000000000); 66 | t.is(toNumber('one trillion'), 1000000000000); 67 | }); 68 | 69 | test('if random complex string units with powers return their numeric counterparts', (t) => { 70 | t.is(toNumber('three thousand four hundred and twelve'), 3412); 71 | t.is(toNumber('seventeen million and eight'), 17000008); 72 | t.is(toNumber('forty billion three hundred and twelve thousand eight hundred and seventy three'), 40000312873); 73 | }); 74 | 75 | test('if unmapped object types return their correct value assignment', (t) => { 76 | t.true(isNaN(toNumber(ERROR))); 77 | t.true(isNaN(toNumber(FUNCTION))); 78 | t.true(isNaN(toNumber(GENERATOR))); 79 | t.true(isNaN(toNumber(PROMISE))); 80 | t.true(isNaN(toNumber(REGEXP))); 81 | t.true(isNaN(toNumber(STRING))); 82 | t.true(isNaN(toNumber(SYMBOL))); 83 | }); 84 | 85 | test('if mapped object types return their correct value assignment', (t) => { 86 | t.is(toNumber(ARRAY), ARRAY.length); 87 | t.is(toNumber(BOOLEAN), 1); 88 | t.is(toNumber(DATE), DATE.valueOf()); 89 | t.is(toNumber(EMPTY_ARRAY), 0); 90 | t.is(toNumber(EMPTY_MAP), 0); 91 | t.is(toNumber(EMPTY_OBJECT), 0); 92 | t.is(toNumber(EMPTY_SET), 0); 93 | t.is(toNumber(EMPTY_STRING), 0); 94 | t.is(toNumber(MAP), MAP.size); 95 | t.is(toNumber(NULL), 0); 96 | t.is(toNumber(OBJECT), Object.keys(OBJECT).length); 97 | t.is(toNumber(SET), SET.size); 98 | t.is(toNumber(UNDEFINED), 0); 99 | }); 100 | -------------------------------------------------------------------------------- /test/_internals/utils.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import types from '../../src/_internals/objectClass'; 4 | 5 | import { 6 | getObjectClass, 7 | isEncapsulatedBy, 8 | throwUnsupportedError, 9 | } from '../../src/_internals/utils'; 10 | 11 | const ARRAY = [1, 2, 3]; 12 | const OBJECTS = { 13 | array: ['foo', 'bar'], 14 | boolean: true, 15 | dataView: new DataView(new ArrayBuffer(2)), 16 | date: new Date(), 17 | error: new Error('Test'), 18 | float32Array: new Float32Array(ARRAY), 19 | float64Array: new Float64Array(ARRAY), 20 | function: () => {}, 21 | * generator() {}, 22 | int8Array: new Int8Array(ARRAY), 23 | int16Array: new Int16Array(ARRAY), 24 | int32Array: new Int32Array(ARRAY), 25 | map: new Map().set('foo', 'bar'), 26 | math: Math, 27 | null: null, 28 | number: 2, 29 | object: {foo: 'bar'}, 30 | promise: Promise.resolve(1), 31 | regexp: /foo/, 32 | set: new Set().add('foo'), 33 | string: 'string', 34 | symbol: Symbol('foo'), 35 | uint8Array: new Uint8Array(ARRAY), 36 | uint8ClampedArray: new Uint8ClampedArray(ARRAY), 37 | uint16Array: new Uint16Array(ARRAY), 38 | uint32Array: new Uint32Array(ARRAY), 39 | undefined, 40 | weakMap: new WeakMap().set({}, 'bar'), 41 | weakSet: new WeakSet().add({foo: 'bar'}), 42 | }; 43 | 44 | test('if types are correct string values', (t) => { 45 | t.is(types.ARRAY, '[object Array]'); 46 | t.is(types.ARRAY_BUFFER, '[object ArrayBuffer]'); 47 | t.is(types.BOOLEAN, '[object Boolean]'); 48 | t.is(types.DATA_VIEW, '[object DataView]'); 49 | t.is(types.DATE, '[object Date]'); 50 | t.is(types.ERROR, '[object Error]'); 51 | t.is(types.FLOAT_32_ARRAY, '[object Float32Array]'); 52 | t.is(types.FLOAT_64_ARRAY, '[object Float64Array]'); 53 | t.is(types.FUNCTION, '[object Function]'); 54 | t.is(types.GENERATOR, '[object GeneratorFunction]'); 55 | t.is(types.INT_8_ARRAY, '[object Int8Array]'); 56 | t.is(types.INT_16_ARRAY, '[object Int16Array]'); 57 | t.is(types.INT_32_ARRAY, '[object Int32Array]'); 58 | t.is(types.MAP, '[object Map]'); 59 | t.is(types.MATH, '[object Math]'); 60 | t.is(types.NULL, '[object Null]'); 61 | t.is(types.NUMBER, '[object Number]'); 62 | t.is(types.OBJECT, '[object Object]'); 63 | t.is(types.PROMISE, '[object Promise]'); 64 | t.is(types.REGEXP, '[object RegExp]'); 65 | t.is(types.SET, '[object Set]'); 66 | t.is(types.STRING, '[object String]'); 67 | t.is(types.SYMBOL, '[object Symbol]'); 68 | t.is(types.UINT_8_ARRAY, '[object Uint8Array]'); 69 | t.is(types.UINT_8_CLAMPED_ARRAY, '[object Uint8ClampedArray]'); 70 | t.is(types.UINT_16_ARRAY, '[object Uint16Array]'); 71 | t.is(types.UINT_32_ARRAY, '[object Uint32Array]'); 72 | t.is(types.UNDEFINED, '[object Undefined]'); 73 | t.is(types.WEAKMAP, '[object WeakMap]'); 74 | t.is(types.WEAKSET, '[object WeakSet]'); 75 | }); 76 | 77 | test('if getObjectClass correctly identifies to object class values', (t) => { 78 | t.is(getObjectClass(OBJECTS.array), types.ARRAY); 79 | t.is(getObjectClass(OBJECTS.boolean), types.BOOLEAN); 80 | t.is(getObjectClass(OBJECTS.dataView), types.DATA_VIEW); 81 | t.is(getObjectClass(OBJECTS.date), types.DATE); 82 | t.is(getObjectClass(OBJECTS.error), types.ERROR); 83 | t.is(getObjectClass(OBJECTS.float32Array), types.FLOAT_32_ARRAY); 84 | t.is(getObjectClass(OBJECTS.float64Array), types.FLOAT_64_ARRAY); 85 | t.is(getObjectClass(OBJECTS.function), types.FUNCTION); 86 | t.is(getObjectClass(OBJECTS.generator), types.GENERATOR); 87 | t.is(getObjectClass(OBJECTS.int8Array), types.INT_8_ARRAY); 88 | t.is(getObjectClass(OBJECTS.int16Array), types.INT_16_ARRAY); 89 | t.is(getObjectClass(OBJECTS.int32Array), types.INT_32_ARRAY); 90 | t.is(getObjectClass(OBJECTS.map), types.MAP); 91 | t.is(getObjectClass(OBJECTS.math), types.MATH); 92 | t.is(getObjectClass(OBJECTS.null), types.NULL); 93 | t.is(getObjectClass(OBJECTS.number), types.NUMBER); 94 | t.is(getObjectClass(OBJECTS.object), types.OBJECT); 95 | t.is(getObjectClass(OBJECTS.promise), types.PROMISE); 96 | t.is(getObjectClass(OBJECTS.regexp), types.REGEXP); 97 | t.is(getObjectClass(OBJECTS.set), types.SET); 98 | t.is(getObjectClass(OBJECTS.string), types.STRING); 99 | t.is(getObjectClass(OBJECTS.symbol), types.SYMBOL); 100 | t.is(getObjectClass(OBJECTS.uint8Array), types.UINT_8_ARRAY); 101 | t.is(getObjectClass(OBJECTS.uint8ClampedArray), types.UINT_8_CLAMPED_ARRAY); 102 | t.is(getObjectClass(OBJECTS.uint16Array), types.UINT_16_ARRAY); 103 | t.is(getObjectClass(OBJECTS.uint32Array), types.UINT_32_ARRAY); 104 | t.is(getObjectClass(OBJECTS.undefined), types.UNDEFINED); 105 | t.is(getObjectClass(OBJECTS.weakMap), types.WEAKMAP); 106 | t.is(getObjectClass(OBJECTS.weakSet), types.WEAKSET); 107 | }); 108 | 109 | test('isEncapsulatedBy tests if string is encapsulated by the correct values', (t) => { 110 | const sameBeginAndEnd = '|foo|'; 111 | const differentBeginAndEnd = '[foo]'; 112 | const notEncapsulated = 'foo'; 113 | 114 | t.true(isEncapsulatedBy(sameBeginAndEnd, '|')); 115 | t.true(isEncapsulatedBy(differentBeginAndEnd, '[', ']')); 116 | t.false(isEncapsulatedBy(notEncapsulated, '|')); 117 | }); 118 | 119 | test('if UnsupportedError throws with the correct message', (t) => { 120 | t.throws(() => { 121 | throwUnsupportedError('Map'); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /src/toString.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import stringify from 'fast-stringify'; 3 | 4 | // constants 5 | import TYPES from './_internals/objectClass'; 6 | 7 | // utils 8 | import { 9 | getObjectClass, 10 | isEncapsulatedBy, 11 | } from './_internals/utils'; 12 | 13 | const HTML_ELEMENT_REGEXP = /\[object (HTML(.*)Element)\]/; 14 | const MATH_PROPERTIES = ['E', 'LN2', 'LN10', 'LOG2E', 'LOG10E', 'PI', 'SQRT1_2', 'SQRT2']; 15 | 16 | /** 17 | * get the string value of the buffer passed 18 | * 19 | * @param {ArrayBuffer} buffer 20 | * @returns {string} 21 | */ 22 | const arrayBufferToString = (buffer) => { 23 | if (typeof Uint16Array === 'undefined') { 24 | return ''; 25 | } 26 | 27 | return String.fromCharCode.apply(null, new Uint16Array(buffer)); 28 | }; 29 | 30 | /** 31 | * strip away [object and ] from return of toString() 32 | * to get the object class 33 | * 34 | * @param {string} type 35 | * @returns {string} 36 | */ 37 | const getObjectType = (type) => type.replace(/^\[object (.+)\]$/, '$1'); 38 | 39 | /** 40 | * get the string value for the object used for stringification 41 | * 42 | * @param {any} object 43 | * @returns {any} 44 | */ 45 | const getValueForStringification = (object) => { 46 | const objectClass = getObjectClass(object); 47 | const type = getObjectType(objectClass); 48 | 49 | switch (objectClass) { 50 | case TYPES.ARGUMENTS: 51 | case TYPES.ARRAY: 52 | case TYPES.OBJECT: 53 | case TYPES.NUMBER: 54 | case TYPES.STRING: 55 | return object; 56 | 57 | case TYPES.ERROR: 58 | case TYPES.NULL: 59 | case TYPES.REGEXP: 60 | case TYPES.UNDEFINED: 61 | return `${object}`; 62 | 63 | case TYPES.DATE: 64 | return object.toISOString(); 65 | 66 | case TYPES.FUNCTION: 67 | case TYPES.GENERATOR: 68 | case TYPES.SYMBOL: 69 | return object.toString(); 70 | 71 | case TYPES.PROMISE: 72 | case TYPES.WEAKMAP: 73 | case TYPES.WEAKSET: 74 | return type; 75 | 76 | case TYPES.MAP: 77 | let newObject = {}; 78 | 79 | object.forEach((value, key) => { 80 | newObject[key] = value; 81 | }); 82 | 83 | return newObject; 84 | 85 | case TYPES.SET: 86 | let newArray = []; 87 | 88 | object.forEach((item) => { 89 | newArray.push(item); 90 | }); 91 | 92 | return newArray; 93 | 94 | case TYPES.ARRAY_BUFFER: 95 | return arrayBufferToString(object); 96 | 97 | case TYPES.DATA_VIEW: 98 | return arrayBufferToString(object.buffer); 99 | 100 | case TYPES.FLOAT_32_ARRAY: 101 | case TYPES.FLOAT_64_ARRAY: 102 | case TYPES.INT_8_ARRAY: 103 | case TYPES.INT_16_ARRAY: 104 | case TYPES.INT_32_ARRAY: 105 | case TYPES.UINT_8_ARRAY: 106 | case TYPES.UINT_8_CLAMPED_ARRAY: 107 | case TYPES.UINT_16_ARRAY: 108 | case TYPES.UINT_32_ARRAY: 109 | return `[${object.join(',')}]`; 110 | 111 | case TYPES.MATH: 112 | let mathObject = {}; 113 | 114 | MATH_PROPERTIES.forEach((prop) => { 115 | mathObject[prop] = object[prop]; 116 | }); 117 | 118 | return mathObject; 119 | 120 | default: 121 | return HTML_ELEMENT_REGEXP.test(objectClass) ? object.textContent : object; 122 | } 123 | }; 124 | 125 | /** 126 | * create the replacer function leveraging closure for 127 | * recursive stack storage 128 | */ 129 | export const replacer = ((stack, undefined, recursiveCounter, index) => (key, value) => { 130 | if (key === '') { 131 | stack = [value]; 132 | recursiveCounter = 0; 133 | 134 | return value; 135 | } 136 | 137 | const objectClass = getObjectClass(value); 138 | 139 | switch (objectClass) { 140 | case TYPES.NUMBER: 141 | case TYPES.STRING: 142 | return value; 143 | 144 | case TYPES.ARGUMENTS: 145 | case TYPES.ARRAY: 146 | case TYPES.OBJECT: 147 | if (!value) { 148 | return value; 149 | } 150 | 151 | if (++recursiveCounter > 255) { 152 | return 'undefined'; 153 | } 154 | 155 | index = stack.indexOf(value); 156 | 157 | if (!~index) { 158 | stack.push(value); 159 | 160 | return value; 161 | } 162 | 163 | return `@@Recursive ${objectClass === TYPES.ARRAY ? '[Array]' : '{Object}'} (${index})`; 164 | 165 | case TYPES.ARRAY_BUFFER: 166 | case TYPES.DATA_VIEW: 167 | case TYPES.DATE: 168 | case TYPES.FLOAT_32_ARRAY: 169 | case TYPES.FLOAT_64_ARRAY: 170 | case TYPES.FUNCTION: 171 | case TYPES.GENERATOR: 172 | case TYPES.INT_8_ARRAY: 173 | case TYPES.INT_16_ARRAY: 174 | case TYPES.INT_32_ARRAY: 175 | case TYPES.ERROR: 176 | case TYPES.MAP: 177 | case TYPES.MATH: 178 | case TYPES.NULL: 179 | case TYPES.PROMISE: 180 | case TYPES.REGEXP: 181 | case TYPES.SET: 182 | case TYPES.SYMBOL: 183 | case TYPES.UINT_8_ARRAY: 184 | case TYPES.UINT_8_CLAMPED_ARRAY: 185 | case TYPES.UINT_16_ARRAY: 186 | case TYPES.UINT_32_ARRAY: 187 | case TYPES.UNDEFINED: 188 | case TYPES.WEAKMAP: 189 | case TYPES.WEAKSET: 190 | return getValueForStringification(value); 191 | 192 | default: 193 | return value; 194 | } 195 | })(); 196 | 197 | /** 198 | * stringify the object passed leveraging the replacer 199 | * 200 | * @param {any} object 201 | * @returns {string} 202 | */ 203 | const toString = (object) => { 204 | const value = stringify(getValueForStringification(object), replacer); 205 | 206 | if (isEncapsulatedBy(value, '"')) { 207 | return value.substring(1, value.length - 1); 208 | } 209 | 210 | return value; 211 | }; 212 | 213 | export default toString; 214 | -------------------------------------------------------------------------------- /src/toNumber.js: -------------------------------------------------------------------------------- 1 | // constants 2 | import { 3 | STOP_POINTS, 4 | TOKENS, 5 | VALUES, 6 | } from './_internals/numbers'; 7 | import { 8 | ARRAY, 9 | BOOLEAN, 10 | DATE, 11 | MAP, 12 | NULL, 13 | NUMBER, 14 | OBJECT, 15 | SET, 16 | STRING, 17 | } from './_internals/objectClass'; 18 | 19 | // utils 20 | import {getObjectClass} from './_internals/utils'; 21 | 22 | const {keys} = Object; 23 | 24 | /** 25 | * remove extraneous portions of the string and eliminate plurals 26 | * 27 | * @param {string} string 28 | * @returns {string} 29 | */ 30 | const sanitize = (string) => 31 | string 32 | .toLowerCase() 33 | .replace(/ and /g, ' ') 34 | .replace(/ +(?= )/g, '') 35 | .replace(/-/g, '') 36 | .replace(/(hundreds|thousands|millions|billions|trillions)/g, (match) => match.slice(0, -1)); 37 | 38 | /** 39 | * get the type (unit, power, special, etc) for the 40 | * numberString 41 | * 42 | * @param {string} numberString 43 | * @returns {string|null} 44 | */ 45 | const getType = (numberString) => { 46 | for (let key in TOKENS) { 47 | if (~TOKENS[key].indexOf(numberString)) { 48 | return key; 49 | } 50 | } 51 | 52 | return null; 53 | }; 54 | 55 | /** 56 | * split the string and make sure each part 57 | * is a valid value 58 | * 59 | * @param {string} string 60 | * @returns {array} 61 | */ 62 | const splitAndValidate = (string) => 63 | string.split(' ').map((value) => { 64 | if (getObjectClass(value) === NULL) { 65 | throw new Error(`${value} is not a valid number.`); 66 | } 67 | 68 | return value; 69 | }); 70 | 71 | /** 72 | * is the leftArray equal to the right array in value 73 | * 74 | * @param {array} leftArray 75 | * @param {array} rightArray 76 | * @returns {boolean} 77 | */ 78 | const isSameArray = (leftArray, rightArray) => { 79 | const leftLength = leftArray.length; 80 | 81 | if (getObjectClass(leftArray) !== ARRAY || getObjectClass(rightArray) !== ARRAY || leftLength !== rightArray.length) { 82 | return false; 83 | } 84 | 85 | let index = -1; 86 | 87 | while (++index < leftLength) { 88 | if (leftArray[index] !== rightArray[index]) { 89 | return false; 90 | } 91 | } 92 | 93 | return true; 94 | }; 95 | 96 | /** 97 | * determine if array contents are considered a stopping point 98 | * 99 | * @param {array} array 100 | * @returns {boolean} 101 | */ 102 | const isStopPoint = (array) => { 103 | const length = STOP_POINTS.length; 104 | 105 | let index = -1; 106 | 107 | while (++index < length) { 108 | if (isSameArray(STOP_POINTS[index], array)) { 109 | return true; 110 | } 111 | } 112 | 113 | return false; 114 | }; 115 | 116 | /** 117 | * group the array of values based on their type 118 | * (units, powers, etc) 119 | * 120 | * @param {array} array 121 | * @returns {array} 122 | */ 123 | const groupTokens = (array) => { 124 | let arrayClone = [...array], 125 | tempArray = [], 126 | groups = [], 127 | typeArray, 128 | currentToken, 129 | currentType; 130 | 131 | while (arrayClone.length) { 132 | currentToken = arrayClone.pop(); 133 | currentType = getType(currentToken); 134 | 135 | tempArray.unshift(currentToken); 136 | 137 | typeArray = tempArray.map((item) => getType(item)); 138 | 139 | if (currentType === 'power' || isStopPoint(typeArray)) { 140 | groups.unshift(tempArray); 141 | 142 | tempArray = []; 143 | } 144 | } 145 | 146 | return groups; 147 | }; 148 | 149 | /** 150 | * calculate sum based on array 151 | * 152 | * @param {array} array 153 | * @returns {number} 154 | */ 155 | const reduceArray = (array) => 156 | array.reduce((sum, current, index) => { 157 | if (array[index + 1] === 100) { 158 | sum += current * 100; 159 | } else if (current !== 100) { 160 | sum += current; 161 | } 162 | 163 | return sum; 164 | }, 0); 165 | 166 | /** 167 | * based on groups, calculate the total value 168 | * 169 | * @param {array} groups 170 | * @returns {number} 171 | */ 172 | const calculateValue = (groups) => { 173 | const maxIndex = groups.length - 1; 174 | 175 | let currentStack = 0; 176 | 177 | return groups.reduce((sum, group, index) => { 178 | if (getType(group[0]) === 'power') { 179 | sum += currentStack * VALUES[group[0]]; 180 | 181 | currentStack = 0; 182 | } else { 183 | currentStack += reduceArray(group.map((item) => VALUES[item])); 184 | 185 | if (index === maxIndex) { 186 | sum += currentStack; 187 | } 188 | } 189 | 190 | return sum; 191 | }, 0); 192 | }; 193 | 194 | /** 195 | * convert object to number with different mappings 196 | * based on object type 197 | * 198 | * if object is a string, try to convert it via parseFloat 199 | * or words-to-number 200 | * 201 | * @param {any} object 202 | * @returns {number} 203 | */ 204 | const toNumber = (object) => { 205 | // eslint-disable-next-line eqeqeq 206 | if (object == null) { 207 | return 0; 208 | } 209 | 210 | const objectClass = getObjectClass(object); 211 | 212 | if (objectClass === NUMBER) { 213 | return object; 214 | } 215 | 216 | if (objectClass === BOOLEAN) { 217 | return object === true ? 1 : 0; 218 | } 219 | 220 | if (objectClass === DATE) { 221 | return object.getTime(); 222 | } 223 | 224 | if (objectClass === ARRAY) { 225 | return object.length; 226 | } 227 | 228 | if (objectClass === OBJECT) { 229 | return keys(object).length; 230 | } 231 | 232 | if (objectClass === MAP || objectClass === SET) { 233 | return object.size; 234 | } 235 | 236 | if (objectClass !== STRING) { 237 | return NaN; 238 | } 239 | 240 | if (object === 'true' || object === 'false') { 241 | return VALUES[object]; 242 | } 243 | 244 | const parsedNumber = parseFloat(object); 245 | 246 | if (parsedNumber === parsedNumber) { 247 | return parsedNumber; 248 | } 249 | 250 | const string = object.trim(); 251 | 252 | if (!string) { 253 | return 0; 254 | } 255 | 256 | const valueAsIs = VALUES[string]; 257 | 258 | if (valueAsIs !== void 0) { 259 | return valueAsIs; 260 | } 261 | 262 | const sanitizedString = sanitize(string); 263 | const splitString = splitAndValidate(sanitizedString); 264 | const groups = groupTokens(splitString); 265 | 266 | return calculateValue(groups) || NaN; 267 | }; 268 | 269 | export default toNumber; 270 | -------------------------------------------------------------------------------- /test/toString.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import toString, {replacer} from '../src/toString'; 4 | 5 | const DATE = new Date(); 6 | const INTEGER_ARRAY = [1, 2, 3]; 7 | const FUNCTION = function value() {}; 8 | const GENERATOR = function* value() {}; 9 | const TEST_VALUES = [ 10 | { 11 | comparator: 'deepEqual', 12 | expectedResult: {}, 13 | expectedString: '{}', 14 | key: '', 15 | value: {}, 16 | }, 17 | { 18 | comparator: 'deepEqual', 19 | expectedResult: ['foo', 'bar'], 20 | expectedString: '{"0":"foo","1":"bar"}', 21 | key: 'arguments', 22 | value: (function() { 23 | // eslint-disable-next-line prefer-rest-params 24 | return arguments; 25 | }('foo', 'bar')), 26 | }, 27 | { 28 | comparator: 'deepEqual', 29 | expectedResult: ['foo', 'bar'], 30 | expectedString: '["foo","bar"]', 31 | key: 'array', 32 | value: ['foo', 'bar'], 33 | }, 34 | { 35 | comparator: 'deepEqual', 36 | expectedResult: `\u0001\u0002\u0003`, 37 | expectedString: `\\u0001\\u0002\\u0003`, 38 | key: 'arrayBuffer', 39 | value: new Uint16Array(INTEGER_ARRAY).buffer, 40 | }, 41 | { 42 | comparator: 'is', 43 | expectedResult: true, 44 | expectedString: 'true', 45 | key: 'boolean', 46 | value: true, 47 | }, 48 | { 49 | comparator: 'deepEqual', 50 | expectedResult: `\u0000`, 51 | expectedString: `\\u0000`, 52 | key: 'dataView', 53 | value: new DataView(new ArrayBuffer(2)), 54 | }, 55 | { 56 | comparator: 'is', 57 | expectedResult: DATE.toISOString(), 58 | expectedString: DATE.toISOString(), 59 | key: 'date', 60 | value: DATE, 61 | }, 62 | { 63 | comparator: 'is', 64 | expectedResult: 'Error: test', 65 | expectedString: 'Error: test', 66 | key: 'error', 67 | value: new Error('test'), 68 | }, 69 | { 70 | comparator: 'deepEqual', 71 | expectedResult: '[1,2,3]', 72 | expectedString: '[1,2,3]', 73 | key: 'float32Array', 74 | value: new Float32Array(INTEGER_ARRAY), 75 | }, 76 | { 77 | comparator: 'deepEqual', 78 | expectedResult: '[1,2,3]', 79 | expectedString: '[1,2,3]', 80 | key: 'float64Array', 81 | value: new Float64Array(INTEGER_ARRAY), 82 | }, 83 | { 84 | comparator: 'is', 85 | expectedResult: FUNCTION.toString(), 86 | expectedString: FUNCTION.toString(), 87 | key: 'function', 88 | value: FUNCTION, 89 | }, 90 | { 91 | comparator: 'is', 92 | expectedResult: GENERATOR.toString(), 93 | expectedString: GENERATOR.toString(), 94 | key: 'generator', 95 | value: GENERATOR, 96 | }, 97 | { 98 | comparator: 'deepEqual', 99 | expectedResult: '[1,2,3]', 100 | expectedString: '[1,2,3]', 101 | key: 'int8Array', 102 | value: new Int8Array(INTEGER_ARRAY), 103 | }, 104 | { 105 | comparator: 'deepEqual', 106 | expectedResult: '[1,2,3]', 107 | expectedString: '[1,2,3]', 108 | key: 'int16Array', 109 | value: new Int16Array(INTEGER_ARRAY), 110 | }, 111 | { 112 | comparator: 'deepEqual', 113 | expectedResult: '[1,2,3]', 114 | expectedString: '[1,2,3]', 115 | key: 'int32Array', 116 | value: new Int32Array(INTEGER_ARRAY), 117 | }, 118 | { 119 | comparator: 'deepEqual', 120 | expectedResult: {foo: 'bar'}, 121 | expectedString: '{"foo":"bar"}', 122 | key: 'map', 123 | value: new Map().set('foo', 'bar'), 124 | }, 125 | { 126 | comparator: 'deepEqual', 127 | expectedResult: { 128 | E: 2.718281828459045, 129 | LN2: 0.6931471805599453, 130 | LN10: 2.302585092994046, 131 | LOG2E: 1.4426950408889634, 132 | LOG10E: 0.4342944819032518, 133 | PI: 3.141592653589793, 134 | SQRT1_2: 0.7071067811865476, 135 | SQRT2: 1.4142135623730951, 136 | }, 137 | expectedString: 138 | '{"E":2.718281828459045,"LN2":0.6931471805599453,"LN10":2.302585092994046,"LOG2E":1.4426950408889634,"LOG10E":0.4342944819032518,"PI":3.141592653589793,"SQRT1_2":0.7071067811865476,"SQRT2":1.4142135623730951}', 139 | key: 'math', 140 | value: Math, 141 | }, 142 | { 143 | comparator: 'is', 144 | expectedResult: 'null', 145 | expectedString: 'null', 146 | key: 'null', 147 | value: null, 148 | }, 149 | { 150 | comparator: 'is', 151 | expectedResult: 12, 152 | expectedString: '12', 153 | key: 'number', 154 | value: 12, 155 | }, 156 | { 157 | comparator: 'deepEqual', 158 | expectedResult: {foo: 'bar'}, 159 | expectedString: '{"foo":"bar"}', 160 | key: 'object', 161 | value: {foo: 'bar'}, 162 | }, 163 | { 164 | comparator: 'is', 165 | expectedResult: 'Promise', 166 | expectedString: 'Promise', 167 | key: 'promise', 168 | value: Promise.resolve(1), 169 | }, 170 | { 171 | comparator: 'is', 172 | expectedResult: '/foo/', 173 | expectedString: '/foo/', 174 | key: 'regexp', 175 | value: /foo/, 176 | }, 177 | { 178 | comparator: 'deepEqual', 179 | expectedResult: ['foo'], 180 | expectedString: '["foo"]', 181 | key: 'set', 182 | value: new Set().add('foo'), 183 | }, 184 | { 185 | comparator: 'is', 186 | expectedResult: 'foo', 187 | expectedString: 'foo', 188 | key: 'string', 189 | value: 'foo', 190 | }, 191 | { 192 | comparator: 'is', 193 | expectedResult: 'Symbol(foo)', 194 | expectedString: 'Symbol(foo)', 195 | key: 'symbol', 196 | value: Symbol('foo'), 197 | }, 198 | { 199 | comparator: 'deepEqual', 200 | expectedResult: '[1,2,3]', 201 | expectedString: '[1,2,3]', 202 | key: 'uint8Array', 203 | value: new Uint8Array(INTEGER_ARRAY), 204 | }, 205 | { 206 | comparator: 'deepEqual', 207 | expectedResult: '[1,2,3]', 208 | expectedString: '[1,2,3]', 209 | key: 'uint8ClampedArray', 210 | value: new Uint8ClampedArray(INTEGER_ARRAY), 211 | }, 212 | { 213 | comparator: 'deepEqual', 214 | expectedResult: '[1,2,3]', 215 | expectedString: '[1,2,3]', 216 | key: 'uint16Array', 217 | value: new Uint16Array(INTEGER_ARRAY), 218 | }, 219 | { 220 | comparator: 'deepEqual', 221 | expectedResult: '[1,2,3]', 222 | expectedString: '[1,2,3]', 223 | key: 'uint32Array', 224 | value: new Uint32Array(INTEGER_ARRAY), 225 | }, 226 | { 227 | comparator: 'is', 228 | expectedResult: 'undefined', 229 | expectedString: 'undefined', 230 | key: 'undefined', 231 | value: undefined, 232 | }, 233 | { 234 | comparator: 'is', 235 | expectedResult: 'WeakMap', 236 | expectedString: 'WeakMap', 237 | key: 'weakMap', 238 | value: new WeakMap().set({}, 'foo'), 239 | }, 240 | { 241 | comparator: 'is', 242 | expectedResult: 'WeakSet', 243 | expectedString: 'WeakSet', 244 | key: 'weakSet', 245 | value: new WeakSet().add({}), 246 | }, 247 | ]; 248 | 249 | test('if replacer provides correct values for different object types', (t) => { 250 | TEST_VALUES.forEach(({comparator, expectedResult, key, value}) => { 251 | t[comparator](replacer(key, value), expectedResult); 252 | }); 253 | }); 254 | 255 | test('if toString uses JSON.stringify with replacer correctly, and falls back to prune when needed', (t) => { 256 | TEST_VALUES.forEach(({comparator, expectedString, key, value}) => { 257 | if (key !== 'generator') { 258 | t[comparator](toString(value), expectedString); 259 | } 260 | }); 261 | 262 | toString(window); 263 | }); 264 | 265 | test('if toString can handle crazy circular objects', (t) => { 266 | try { 267 | toString(window); 268 | 269 | t.pass(); 270 | } catch (error) { 271 | t.fail(error); 272 | } 273 | }); 274 | 275 | test.todo('Figure out how to compare generators and their string values'); 276 | -------------------------------------------------------------------------------- /DEV_ONLY/App.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill'; 2 | 3 | import React from 'react'; 4 | import { 5 | render 6 | } from 'react-dom'; 7 | 8 | import to from '../src'; 9 | 10 | const fooBar = { 11 | foo: {} 12 | }; 13 | 14 | fooBar.foo = fooBar; 15 | 16 | const fn = (blah) => { 17 | console.log('test', blah); 18 | }; 19 | 20 | const array = [ 21 | fooBar 22 | ]; 23 | 24 | array.push(array); 25 | 26 | const num = 1; 27 | const string = 'foo'; 28 | 29 | const testPerformance = (message, convertTo, object) => { 30 | const length = 1000; 31 | 32 | let index = -1; 33 | 34 | const start = Date.now(); 35 | 36 | while (++index < length) { 37 | to(convertTo, object); 38 | } 39 | 40 | console.log(`${message} took ${Date.now() - start} milliseconds to do ${length} conversions.`); 41 | }; 42 | 43 | const timeAction = (message, convertTo, object) => { 44 | const fullMessage = `${message} to ${convertTo}`; 45 | 46 | let convertedObject; 47 | 48 | console.group(fullMessage); 49 | 50 | console.time(fullMessage); 51 | convertedObject = to(convertTo, object); 52 | console.timeEnd(fullMessage); 53 | 54 | console.log(convertedObject); 55 | 56 | // testPerformance(message, convertTo, object); 57 | 58 | console.groupEnd(fullMessage); 59 | }; 60 | 61 | timeAction('object', 'string', {foo: 'bar'}); 62 | timeAction('recursive object', 'string', fooBar); 63 | timeAction('function', 'string', fn); 64 | timeAction('number', 'string', num); 65 | timeAction('already string', 'string', string); 66 | timeAction('array', 'string', ['foo', 'bar']); 67 | timeAction('recursive array', 'string', array); 68 | timeAction('undefined', 'string', undefined); 69 | timeAction('null', 'string', null); 70 | timeAction('date', 'string', new Date()); 71 | 72 | 73 | timeAction('already number', 'number', 1); 74 | timeAction('numeric string', 'number', '1'); 75 | timeAction('simple string number (thirteen)', 'number', 'thirteen'); 76 | timeAction('complex string number (two billion three hundred sixty one million fifty three thousand and seven hundred ninety nine)', 'number', 'two billion three hundred sixty one million fifty three thousand and seven hundred ninety nine'); 77 | timeAction('array', 'number', ['foo', 'bar']); 78 | timeAction('object', 'number', {foo: 'bar'}); 79 | timeAction('boolean (true)', 'number', true); 80 | timeAction('boolean (false)', 'number', false); 81 | timeAction('string boolean (true)', 'number', 'true'); 82 | timeAction('string boolean (false)', 'number', 'false'); 83 | timeAction('date', 'number', new Date()); 84 | timeAction('undefined', 'number', undefined); 85 | timeAction('null', 'number', null); 86 | 87 | timeAction('already array', 'array', ['foo', 'bar', 'baz']); 88 | timeAction('object', 'array', {foo: 'bar', bar: 'baz'}); 89 | timeAction('string', 'array', 'foo'); 90 | timeAction('stringified array', 'array', JSON.stringify(['foo', 'bar'])); 91 | timeAction('number', 'array', 1); 92 | timeAction('date', 'array', new Date()); 93 | timeAction('undefined', 'array', undefined); 94 | timeAction('null', 'array', null); 95 | 96 | timeAction('already boolean', 'boolean', true); 97 | timeAction('string', 'boolean', 'stuff'); 98 | timeAction('empty string', 'boolean', ''); 99 | timeAction('object', 'boolean', {foo: 'bar'}); 100 | timeAction('empty object', 'boolean', {}); 101 | timeAction('number (true)', 'boolean', 1); 102 | timeAction('number (false)', 'boolean', 0); 103 | timeAction('date', 'boolean', new Date()); 104 | timeAction('undefined', 'boolean', undefined); 105 | timeAction('null', 'boolean', null); 106 | 107 | timeAction('already object', 'object', {foo: 'bar'}); 108 | timeAction('array', 'object', ['foo', 'bar']); 109 | timeAction('number', 'object', 1); 110 | timeAction('string', 'object', 'foo'); 111 | timeAction('map', 'object', new Map([['foo', 'bar'], ['bar', 'baz']])); 112 | timeAction('set', 'object', new Set(['foo', 'bar', 'baz'])); 113 | timeAction('date', 'object', new Date()); 114 | timeAction('undefined', 'object', undefined); 115 | timeAction('null', 'object', null); 116 | 117 | timeAction('already date', 'date', new Date()); 118 | timeAction('array', 'date', [1985, 0, 1]); 119 | timeAction('array of bad values', 'date', ['foo', 'bar', 'baz']); 120 | timeAction('object', 'date', { 121 | day: 15, 122 | hour: 3, 123 | minutes: 4, 124 | month: 3, 125 | seconds: 14, 126 | year: 2013 127 | }); 128 | timeAction('number', 'date', 1400000000000); 129 | timeAction('good string', 'date', new Date().toISOString()); 130 | timeAction('bad string', 'date', 'not a date'); 131 | timeAction('map', 'date', new Map([['foo', 'bar'], ['bar', 'baz']])); 132 | timeAction('set', 'date', new Set(['foo', 'bar', 'baz'])); 133 | timeAction('boolean', 'date', true); 134 | timeAction('undefined', 'date', undefined); 135 | timeAction('null', 'date', null); 136 | 137 | timeAction('already error', 'error', new Error('test')); 138 | timeAction('string', 'error', 'foo'); 139 | timeAction('number', 'error', 10); 140 | timeAction('object', 'error', {foo: 'bar'}); 141 | timeAction('array', 'error', ['foo', 'bar']); 142 | timeAction('function', 'error', () => {}); 143 | timeAction('map', 'error', new Map([['foo', 'bar'], ['bar', 'baz']])); 144 | timeAction('set', 'error', new Set(['foo', 'bar', 'baz'])); 145 | timeAction('boolean', 'error', true); 146 | timeAction('undefined', 'error', undefined); 147 | timeAction('null', 'error', null); 148 | 149 | timeAction('already function', 'function', function(foo) { return foo; }); 150 | timeAction('object', 'function', {foo: 'bar'}); 151 | timeAction('array', 'function', ['foo', 'bar']); 152 | timeAction('map', 'function', new Map([['foo', 'bar'], ['bar', 'baz']])); 153 | timeAction('set', 'function', new Set(['foo', 'bar', 'baz'])); 154 | timeAction('boolean', 'function', true); 155 | timeAction('undefined', 'function', undefined); 156 | timeAction('null', 'function', null); 157 | 158 | timeAction('already generator', 'generator', function* (foo) { yield foo; }); 159 | timeAction('object', 'generator', {foo: 'bar'}); 160 | timeAction('array', 'generator', ['foo', 'bar']); 161 | timeAction('map', 'generator', new Map([['foo', 'bar'], ['bar', 'baz']])); 162 | timeAction('set', 'generator', new Set(['foo', 'bar', 'baz'])); 163 | timeAction('boolean', 'generator', true); 164 | timeAction('undefined', 'generator', undefined); 165 | timeAction('null', 'generator', null); 166 | 167 | timeAction('already symbol', 'symbol', Symbol('foo')); 168 | timeAction('string', 'symbol', 'foo'); 169 | timeAction('number', 'symbol', 1); 170 | timeAction('array', 'symbol', ['foo', 'bar']); 171 | timeAction('object', 'symbol', {foo: 'bar'}); 172 | timeAction('map', 'symbol', new Map([['foo', 'bar'], ['bar', 'baz']])); 173 | timeAction('set', 'symbol', new Set(['foo', 'bar', 'baz'])); 174 | timeAction('boolean', 'symbol', true); 175 | timeAction('undefined', 'symbol', undefined); 176 | timeAction('null', 'symbol', null); 177 | 178 | timeAction('already map', 'map', new Map()); 179 | timeAction('object', 'map', {foo: 'bar', bar: 'baz'}); 180 | timeAction('array', 'map', ['foo', 'bar', 'baz']); 181 | timeAction('string', 'map', 'foo'); 182 | timeAction('number', 'map', 1); 183 | timeAction('set', 'map', new Set(['foo', 'bar', 'baz'])); 184 | timeAction('boolean', 'map', true); 185 | timeAction('undefined', 'map', undefined); 186 | timeAction('null', 'map', null); 187 | 188 | timeAction('already null', 'null', null); 189 | timeAction('undefined', 'null', undefined); 190 | timeAction('string', 'null', 'foo'); 191 | timeAction('number', 'null', 1); 192 | timeAction('array', 'null', ['foo', 'bar']); 193 | timeAction('object', 'null', {foo: 'bar'}); 194 | timeAction('map', 'null', new Map([['foo', 'bar'], ['bar', 'baz']])); 195 | timeAction('set', 'null', new Set(['foo', 'bar', 'baz'])); 196 | timeAction('boolean', 'symbol', true); 197 | 198 | const promise = new Promise((resolve) => { 199 | resolve(10); 200 | }); 201 | 202 | timeAction('already promise', 'promise', promise); 203 | timeAction('object', 'promise', {foo: 'bar', bar: 'baz'}); 204 | timeAction('array', 'promise', ['foo', 'bar', 'baz']); 205 | timeAction('string', 'promise', 'foo'); 206 | timeAction('number', 'promise', 1); 207 | timeAction('map', 'promise', new Map([['foo', 'bar'], ['bar', 'baz']])); 208 | timeAction('set', 'promise', new Set(['foo', 'bar', 'baz'])); 209 | timeAction('boolean', 'promise', true); 210 | timeAction('undefined', 'promise', undefined); 211 | timeAction('null', 'promise', null); 212 | 213 | timeAction('already regexp', 'regexp', /foo/); 214 | timeAction('string', 'regexp', 'foo'); 215 | timeAction('regexp as string', 'regexp', '/foo/'); 216 | timeAction('number', 'regexp', '10'); 217 | timeAction('array', 'regexp', ['foo', 'bar']); 218 | timeAction('object', 'regexp', {foo: 'bar'}); 219 | timeAction('map', 'regexp', new Map([['foo', 'bar'], ['bar', 'baz']])); 220 | timeAction('set', 'regexp', new Set(['foo', 'bar', 'baz'])); 221 | timeAction('boolean', 'regexp', true); 222 | timeAction('undefined', 'regexp', undefined); 223 | timeAction('null', 'regexp', null); 224 | 225 | timeAction('already set', 'set', new Set(['foo', 'bar', 'baz'])); 226 | timeAction('object', 'set', {foo: 'bar', bar: 'baz'}); 227 | timeAction('array', 'set', ['foo', 'bar', 'baz']); 228 | timeAction('string', 'set', 'foo'); 229 | timeAction('number', 'set', 1); 230 | timeAction('map', 'set', new Map([['foo', 'bar'], ['bar', 'baz']])); 231 | timeAction('boolean', 'set', true); 232 | timeAction('undefined', 'set', undefined); 233 | timeAction('null', 'set', null); 234 | 235 | timeAction('already undefined', 'undefined', undefined); 236 | timeAction('null', 'undefined', undefined); 237 | timeAction('string', 'undefined', 'foo'); 238 | timeAction('number', 'undefined', 1); 239 | timeAction('array', 'undefined', ['foo', 'bar']); 240 | timeAction('object', 'undefined', {foo: 'bar'}); 241 | timeAction('map', 'undefined', new Map([['foo', 'bar'], ['bar', 'baz']])); 242 | timeAction('set', 'undefined', new Set(['foo', 'bar', 'baz'])); 243 | timeAction('boolean', 'undefined', true); 244 | 245 | const App = () => { 246 | return ( 247 |
248 | App 249 |
250 | ); 251 | }; 252 | 253 | const div = document.createElement('div'); 254 | 255 | div.id = 'app-container'; 256 | 257 | render(( 258 | 259 | ), div); 260 | 261 | document.body.appendChild(div); -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # convertify API 2 | 3 | Below is the details regarding what kind of objects to expect when making conversions. 4 | 5 | **Table of Contents** 6 | 7 | * [Note](#note) 8 | * [to.array](#toarray) 9 | * [to.boolean](#toboolean) 10 | * [to.date](#todate) 11 | * [to.error](#toerror) 12 | * [to.function](#tofunction) 13 | * [to.generator](#togenerator) 14 | * [to.map](#tomap) 15 | * [to.null](#tonull) 16 | * [to.number](#tonumber) 17 | * [to.object](#toobject) 18 | * [to.promise](#topromise) 19 | * [to.regexp](#toregexp) 20 | * [to.set](#toset) 21 | * [to.string](#tostring) 22 | * [to.symbol](#tosymbol) 23 | * [to.undefined](#toundefined) 24 | 25 | #### Note 26 | 27 | if the object passed is the same object class as is attempting to convert to, the original object itself is returned 28 | 29 | ```javascript 30 | const object = {foo: 'bar'}; 31 | const convertedObject = to.object(object); 32 | 33 | console.assert(object === convertedObject); // true 34 | ``` 35 | 36 | #### to.array 37 | (*toArray* when imported alone) 38 | 39 | * if the object is a standard `Object`, or ES2015 `Map` / `Set`, an array of the values from the `key: value` pairs are returned 40 | * `to.array({foo: 'bar'}) => ['bar']` 41 | * `to.array(new Map().set('foo', 'bar')) => ['bar']` 42 | * `to.array(new Set().add('bar')) => ['bar']` 43 | * if the object is a `String` and it represents a `JSON` object, a parsed value is returned 44 | * `to.array('{foo: \"bar\"}') => {foo: 'bar'}` 45 | * in all other cases, the object is wrapped in an array 46 | * `to.array('foo') => ['foo']` 47 | 48 | #### to.boolean 49 | (*toBoolean* when imported alone) 50 | 51 | * if the object is an `Array`, it returns based on the array having length 52 | * `to.boolean(['foo']) => true` 53 | * `to.boolean([]) => false` 54 | * if the object is an `Object`, it returns based on the object having keys 55 | * `to.boolean({foo: 'bar}) => true` 56 | * `to.boolean({}) => false` 57 | * if the objec is a `Map` / `Set`, it returns based on the object having size 58 | * `to.boolean(new Map().set('foo', 'bar')) => true` 59 | * `to.boolean(new Set().add('foo') => true` 60 | * `to.boolean(new Map()) => false` 61 | * `to.boolean(new Set()) => false` 62 | * in all other cases, the object is implictly converted to a boolean 63 | * `to.boolean('foo') => true` 64 | * `to.boolean('') => false` 65 | 66 | #### to.date 67 | (*toDate* when imported alone) 68 | 69 | * if the object is an `Array` or `Set`, it applies the values as arguments to the `Date` constructor 70 | * `to.date([2000, 0, 1]) => Sat Jan 01 2000 00:00:00 GMT-0500 (EST)` 71 | * `to.date(new Set().add(2000).add(0).add(1)) => Sat Jan 01 2000 00:00:00 GMT-0500 (EST)` 72 | * if the object is an `Object` or `Map`, it looks for the keys and tries to map them to the arguments to the `Date` constructor 73 | * `to.date({year: 2000, month: 0, day: 1}) => Sat Jan 01 2000 00:00:00 GMT-0500 (EST)` 74 | * `to.date(new Map().set('year', 2000).set('month', 9).set('day', 1)) => Sat Jan 01 2000 00:00:00 GMT-0500 (EST)` 75 | * Valid keys are: 76 | * year 77 | * month 78 | * day 79 | * hour 80 | * minutes 81 | * seconds 82 | * milliseconds 83 | * if the object is a `Boolean` or `Symbol`, an `Invalid Date` is returned 84 | * If the object is `null`, `undefined`, or an empty `String`, a new date (as if no parameters were passed to the constructor) is returned 85 | * in all other cases, the object is passed to the `Date` constructor directly 86 | 87 | #### to.error 88 | (*toError* when imported alone) 89 | 90 | * in all cases, the object is stringified via the library's `toString` and that is applied as the message 91 | 92 | #### to.function 93 | (*toFunction* when imported alone) 94 | 95 | * in all cases, the object passed is wrapped in an anonymous function 96 | 97 | #### to.generator 98 | (*toGenerator* when imported alone) 99 | 100 | * if the object is an `Array` or `Set`, an anonymous generator that will yield each value in the array is returned 101 | * if the object is an `Object` or `Map`, an anonymous generator that will yield each value from the `key: value` pairs is returned 102 | * in all other cases, a generator that will yield the original object is returned 103 | 104 | ```javascript 105 | const array = ['foo', 'bar']; 106 | const object = {foo: 'bar', bar: 'baz'} 107 | const string = 'foo'; 108 | 109 | const arrayGenerator = to.generator(array); 110 | 111 | console.log(arrayGenerator.next()); // {value: 'foo', done: false} 112 | 113 | const objectGenerator = to.generator(object); 114 | 115 | console.log(objectGenerator.next()); // {value: 'foo', done: false} 116 | 117 | const stringGenerator = to.generator(string); 118 | 119 | console.log(stringGenerator.next()); // {value: 'foo', done: true} 120 | ``` 121 | 122 | #### to.map 123 | (*toMap* when imported alone) 124 | 125 | * if the object is an `Array` or `Set`, the indices of the values are used when creating the `key => value` pairs 126 | * `to.map(['foo', 'bar']) => {0 => 'foo', 1 => 'bar'}` 127 | * `to.map(new Set().add('foo').add('bar')) => {0 => 'foo', 1 => 'bar'}` 128 | * if the object is an `Object`, the same `key: value` pairs are used 129 | * `to.map({foo: 'bar}) => {'foo' => 'bar'}` 130 | * in all other cases, the object is given as the value for the key `0` 131 | * `to.map('foo') => {0 => 'foo'}` 132 | 133 | #### to.null 134 | (*toNull* when imported alone) 135 | 136 | * in all cases, the value returned is `null` 137 | 138 | #### to.number 139 | (*toNumber* when imported alone) 140 | 141 | * if the object passed is a `Boolean`, the binary values are returned 142 | * `to.number(true) => 1` 143 | * `to.number(false) => 0` 144 | * if the object passed is a `Date`, the `valueOf` is returned 145 | * `to.number(new Date(2000, 0, 1)) => 946702800000` 146 | * if the object passed is `null` or `undefined`, `0` is returned 147 | * if the object passed is an `Array`, the length is returned 148 | * `to.number(['foo', 'bar']) => 2` 149 | * if the object passed is an `Object`, the number of keys is returned 150 | * `to.number({foo: 'bar'}) => 1` 151 | * if the object passed is a `Map` or `Set`, the size is returned 152 | * `to.number(new Map().set('foo', 'bar')) => 1` 153 | * `to.number(new Set()) => 0` 154 | * if the object passed is a `String`, then several things can happen: 155 | * if it is a numeric string, it returns the parsed value 156 | * `to.number('123') => 123` 157 | * if it is an empty string, it returns `0` 158 | * if it is a string of words representing numbers, the numeric value described is returned 159 | * `to.number('three hundred twenty four') => 324` 160 | * `to.number('ten billion six hundred forty three thousand eight hundred and four') => 10000643804` 161 | * in all other cases, `NaN` is returned 162 | 163 | #### to.object 164 | (*toObject* when imported alone) 165 | 166 | * if the object is an `Array` or `Set`, indices are used as keys in the `key: value` pairs 167 | * `to.object(['foo', 'bar']) => {'0': 'foo', '1': 'bar'}` 168 | * if the object is a `Map`, the `key: value` pairs are translated directly (if the key is not a string, it is stringified via the library's `toString` 169 | * `to.object(new Map().set('foo', 'bar')) => {'foo': 'bar'}` 170 | * `to.object(new Map().set({foo: 'bar'}, 'baz') => {'{"foo":"bar"}': 'baz'}` 171 | * if the object is a `String` and in the format of valid `JSON`, the parsed `JSON` is returned 172 | * `to.object('{"foo":"bar"}') => {foo: 'bar'}` 173 | * in all other cases, the object is used as a value for key `0` 174 | * `to.object('foo') => {'0': 'foo'}` 175 | 176 | #### to.promise 177 | (*toPromise* when imported alone) 178 | 179 | * in all cases, the object is wrapped in `Promise.resolve()` 180 | 181 | #### to.regexp 182 | (*toRegExp* when imported alone) 183 | 184 | * if the object is a `String` then one of two things happens: 185 | * if the string is in the format of a RegExp (starts and ends with `/`) then the leading / trailing slashes are stripped and the value is used 186 | * `to.regexp('/\(foo\)/') => /\(foo\)/` 187 | * otherwise, the string is used directly 188 | * `to.regexp('foo') => /foo/` 189 | * in all other cases, the object is stringified via the library's `toString` and used directly 190 | * `to.regexp({foo: 'bar'}) => /{"foo":"bar"}/` 191 | 192 | #### to.set 193 | (*toSet* when imported alone) 194 | 195 | * if the object is an `Array`, then the values are applied directly to the constructor 196 | * `to.set(['foo', 'bar']) => {'foo', 'bar'}` 197 | * if the object is an `Object` or `Map`, then the values from the `key: value` pairs are used 198 | * `to.set({foo: 'bar'}) => {'bar'}` 199 | * `to.set(new Map().set('foo', 'bar')) => {'bar'}` 200 | * in all other cases, the object is saved as the first value in the set 201 | * `to.set('foo') => {'foo'}` 202 | 203 | #### to.string 204 | (*toString* when imported alone) 205 | 206 | * if the object is an `Arguments`, `Array`, `Object`, `Number`, or `String`, then `JSON.stringify` is applied directly 207 | * `to.string(['foo', 'bar']) => '["foo","bar"]'` 208 | * `to.string({foo: 'bar'}) => '{"foo":"bar"}'` 209 | * `to.string(1) => '1'` 210 | * `to.string('foo') => 'foo'` 211 | * if the object is an `Error` or `RegExp`, or is `null` or `undefined`, implict string conversion is used 212 | * `to.string(new Error('foo')) => 'Error: foo'` 213 | * `to.string(/foo/) => '/foo/'` 214 | * `to.string(null) => 'null'` 215 | * `to.string(undefined) => 'undefined'` 216 | * if the object is a `Date`, then the `toISOString` method is used 217 | * `to.string(new Date(2000, 0, 1)) => 'Sat Jan 01 2000 00:00:00 GMT-0500 (EST)'` 218 | * if the object is a `Function`, `GeneratorFunction`, or `Symbol`, the native `toString` method is used 219 | * `to.string(function foo() {}) => 'function foo() {}'` 220 | * `to.string(Symbol('foo')) => 'Symbol(foo)'` 221 | * if the object is a `Promise`, `WeakMap`, or `WeakSet`, the object class is returned 222 | * this is because the values of these objects are impossible to see and therefore uniquely stringify 223 | * `to.string(Promise.resolve(1)) => 'Promise'` 224 | * `to.string(new WeakMap()) => 'WeakMap'` 225 | * `to.string(new WeakSet()) => 'WeakSet'` 226 | * if the object is a `Map`, an object of `key: value` pairs is stringified as `JSON` 227 | * `to.string(new Map().set({foo: 'bar'}, 'baz')) => '{{"foo":"bar"}:"baz}'` 228 | * if the object is a `Set`, an array of values is stringified as `JSON` 229 | * `to.string(new Set().add('foo').add('bar')) => '["foo","bar"]'` 230 | * if the object is an `ArrayBuffer`, a `Uint16Array` is created from the buffer and converted to charCodes 231 | * `to.string(new Uint16Array([1,2,3]).buffer) => '\\u0001\\u0002\\u0003'` 232 | * if the object is a `DataView`, the buffer from it is applied with the same technique as `ArrayBuffer` 233 | * if the object is a TypedArray (`Float32Array`, `Int16Array`, `Uint8Array`, etc), the native `join` method is used 234 | * `to.string(new Uint8Array([1,2,3])) => '[1,2,3]'` 235 | * if the object is the `Math` object, an object of `key: value` pairs for its constants is used 236 | * `to.string(Math) => '{"E":2.718281828459045,"LN2":0.6931471805599453,"LN10":2.302585092994046,"LOG2E":1.4426950408889634,"LOG10E":0.4342944819032518,"PI":3.141592653589793,"SQRT1_2":0.7071067811865476,"SQRT2":1.4142135623730951}'` 237 | * if the object is an `HTMLElement` (or a subset, such as `HTMLDivElement`), then is `textContent is used 238 | 239 | ```javascript 240 | const div = document.createElement('div'); 241 | 242 | div.innerHTML = 'Hello'; 243 | 244 | console.log(to.string(div)); // Hello 245 | ``` 246 | * in all other cases, the object is stringified as `JSON` 247 | 248 | #### to.symbol 249 | (*toSymbol* when imported alone) 250 | 251 | * if the object is a string, it is applied to the symbol directly 252 | * `to.symbol('foo') => Symbol(foo)` 253 | * in all other cases, the object is stringified using the library's `toString` method and applied to the symbol 254 | * `to.symbol(['foo', 'bar']) => Symbol(["foo","bar"])` 255 | 256 | #### to.undefined 257 | (*toUndefined* when imported alone) 258 | 259 | * in all cases, the value returned is `undefined` 260 | --------------------------------------------------------------------------------