diff --git a/API.md b/API.md index 515e53da..9776c74a 100755 --- a/API.md +++ b/API.md @@ -7,15 +7,11 @@ * [applyToDefaults](#applytodefaultsdefaults-options-isnulloverride "applyToDefaults") * [applyToDefaultsWithShallow](#applytodefaultswithshallowdefaults-options-keys "applyToDefaultsWithShallow") * [deepEqual](#deepequalb-a-options "deepEqual") - * [unique](#uniquearray-key "unique") - * [mapToObject](#maptoobjectarray-key "mapToObject") * [intersect](#intersectarray1-array2 "intersect") * [contain](#containref-values-options "contain") * [flatten](#flattenarray-target "flatten") * [reach](#reachobj-chain-options "reach") * [reachTemplate](#reachtemplateobj-template-options "reachTemplate") - * [transform](#transformobj-transform-options "transform") - * [shallow](#shallowsource "shallow") * [stringify](#stringifyobj "stringify") * [Bench](#bench "Bench") * [Escaping Characters](#escaping-characters "Escaping Characters") @@ -170,34 +166,6 @@ Hoek.deepEqual(Object.create(null), {}, { prototype: false }); //results in true Hoek.deepEqual(Object.create(null), {}); //results in false ``` -### unique(array, key) - -Remove duplicate items from Array - -```javascript - -var array = [1, 2, 2, 3, 3, 4, 5, 6]; - -var newArray = Hoek.unique(array); // results in [1,2,3,4,5,6] - -array = [{id: 1}, {id: 1}, {id: 2}]; - -newArray = Hoek.unique(array, "id"); // results in [{id: 1}, {id: 2}] -``` - -### mapToObject(array, key) - -Convert an Array into an Object - -```javascript - -var array = [1,2,3]; -var newObject = Hoek.mapToObject(array); // results in {"1": true, "2": true, "3": true} - -array = [{id: 1}, {id: 2}]; -newObject = Hoek.mapToObject(array, "id"); // results in {"1": true, "2": true} -``` - ### intersect(array1, array2) Find the common unique items in two arrays @@ -293,48 +261,6 @@ var obj = {a : {b : { c : 1}}}; Hoek.reachTemplate(obj, '1+{a.b.c}=2'); // returns '1+1=2' ``` -### transform(obj, transform, [options]) - -Transforms an existing object into a new one based on the supplied `obj` and `transform` map. `options` are the same as the `reach` options. The first argument can also be an array of objects. In that case the method will return an array of transformed objects. Note that `options.separator` will be respected for the keys in the transform object as well as values. - -```javascript -var source = { - address: { - one: '123 main street', - two: 'PO Box 1234' - }, - title: 'Warehouse', - state: 'CA' -}; - -var result = Hoek.transform(source, { - 'person.address.lineOne': 'address.one', - 'person.address.lineTwo': 'address.two', - 'title': 'title', - 'person.address.region': 'state' -}); -// Results in -// { -// person: { -// address: { -// lineOne: '123 main street', -// lineTwo: 'PO Box 1234', -// region: 'CA' -// } -// }, -// title: 'Warehouse' -// } -``` - -### shallow(source) - -Returns a new object with shallow copies of all source properties where: -- `source` - the source to be copied. - -```javascript -var shallow = Hoek.shallow({ a: { b: 1 } }); -``` - ### stringify(obj) Converts an object to string using the built-in `JSON.stringify()` method with the difference that any errors are caught diff --git a/README.md b/README.md old mode 100644 new mode 100755 index a5c05086..dba9868e --- a/README.md +++ b/README.md @@ -1,25 +1,14 @@ ![hoek Logo](https://raw.github.com/hapijs/hoek/master/images/hoek.png) -Utility methods for the hapi ecosystem. This module is not intended to solve every problem for everyone, but rather as a central place to store hapi-specific methods. If you're looking for a general purpose utility module, check out [lodash](https://github.com/lodash/lodash) or [underscore](https://github.com/jashkenas/underscore). +Utility methods for the hapi ecosystem. This module is not intended to solve every problem for +everyone, but rather as a central place to store hapi-specific methods. If you're looking for a +general purpose utility module, check out [lodash](https://github.com/lodash/lodash) or +[underscore](https://github.com/jashkenas/underscore). [![Build Status](https://secure.travis-ci.org/hapijs/hoek.svg)](http://travis-ci.org/hapijs/hoek) Lead Maintainer: [Gil Pedersen](https://github.com/kanongil) -## Usage - -The *Hoek* library contains some common functions used within the hapi ecosystem. It comes with useful methods for Arrays (clone, merge, applyToDefaults), Objects (removeKeys, copy), Asserting and more. - -For example, to use Hoek to set configuration with default options: -```javascript -const Hoek = require('hoek'); - -const default = {url : "www.github.com", port : "8000", debug : true}; - -const config = Hoek.applyToDefaults(default, {port : "3000", admin : true}); - -// In this case, config would be { url: 'www.github.com', port: '3000', debug: true, admin: true } -``` ## Documentation diff --git a/lib/index.js b/lib/index.js index 717759b3..2bdcdde8 100755 --- a/lib/index.js +++ b/lib/index.js @@ -371,78 +371,43 @@ exports.deepEqual = function (obj, ref, options, seen) { }; -// Remove duplicate items from array +// Find the common unique items in two arrays -exports.unique = (array, key) => { +exports.intersect = function (array1, array2, justFirst) { - let result; - if (key) { - result = []; - const index = new Set(); - array.forEach((item) => { + if (!array1 || + !array2) { - const identifier = item[key]; - if (!index.has(identifier)) { - index.add(identifier); - result.push(item); - } - }); - } - else { - result = Array.from(new Set(array)); + return (justFirst ? null : []); } - return result; -}; - - -// Convert array into object - -exports.mapToObject = function (array, key) { - - if (!array) { - return null; - } + const common = []; + const hash = (Array.isArray(array1) ? new Set(array1) : array1); + const found = new Set(); + for (const value of array2) { + if (internals.has(hash, value) && + !found.has(value)) { - const obj = {}; - for (let i = 0; i < array.length; ++i) { - if (key) { - if (array[i][key]) { - obj[array[i][key]] = true; + if (justFirst) { + return value; } - } - else { - obj[array[i]] = true; + + common.push(value); + found.add(value); } } - return obj; + return (justFirst ? null : common); }; -// Find the common unique items in two arrays - -exports.intersect = function (array1, array2, justFirst) { - - if (!array1 || !array2) { - return []; - } - - const common = []; - const hash = (Array.isArray(array1) ? exports.mapToObject(array1) : array1); - const found = {}; - for (let i = 0; i < array2.length; ++i) { - if (hash[array2[i]] && !found[array2[i]]) { - if (justFirst) { - return array2[i]; - } +internals.has = function (ref, key) { - common.push(array2[i]); - found[array2[i]] = true; - } + if (typeof ref.has === 'function') { + return ref.has(key); } - return (justFirst ? null : common); + return ref[key] !== undefined; }; @@ -751,50 +716,6 @@ exports.once = function (method) { exports.ignore = function () { }; -exports.transform = function (source, transform, options) { - - exports.assert(source === null || source === undefined || typeof source === 'object' || Array.isArray(source), 'Invalid source object: must be null, undefined, an object, or an array'); - const separator = (typeof options === 'object' && options !== null) ? (options.separator || '.') : '.'; - - if (Array.isArray(source)) { - const results = []; - for (let i = 0; i < source.length; ++i) { - results.push(exports.transform(source[i], transform, options)); - } - - return results; - } - - const result = {}; - const keys = Object.keys(transform); - - for (let i = 0; i < keys.length; ++i) { - const key = keys[i]; - const path = key.split(separator); - const sourcePath = transform[key]; - - exports.assert(typeof sourcePath === 'string', 'All mappings must be "." delineated strings'); - - let segment; - let res = result; - - while (path.length > 1) { - segment = path.shift(); - if (!res[segment]) { - res[segment] = {}; - } - - res = res[segment]; - } - - segment = path.shift(); - res[segment] = exports.reach(source, sourcePath, options); - } - - return result; -}; - - exports.uniqueFilename = function (path, extension) { if (extension) { @@ -821,12 +742,6 @@ exports.stringify = function (...args) { }; -exports.shallow = function (source) { - - return Object.assign({}, source); -}; - - exports.wait = function (timeout) { return new Promise((resolve) => setTimeout(resolve, timeout)); diff --git a/test/index.js b/test/index.js index 116727ff..70267652 100755 --- a/test/index.js +++ b/test/index.js @@ -35,38 +35,6 @@ const nestedObj = { z: new Date(1378775452757) }; -internals.unique = { - item: { - objects: [nestedObj, { z: 'z' }] - } -}; - -internals.unique.objectsByKey = { - dups: [internals.unique.item.objects[0], internals.unique.item.objects[1], internals.unique.item.objects[0]], - result: [internals.unique.item.objects[0], internals.unique.item.objects[1]] -}; - -internals.unique.objects = { - dups: [internals.unique.item.objects[1], internals.unique.item.objects[0], internals.unique.item.objects[0]], - result: [internals.unique.item.objects[1], internals.unique.item.objects[0]] -}; - -internals.unique.integers = { - dups: [1, 2, 3, 2, 2, 1, 3, 4, 5], - result: [1, 2, 3, 4, 5] -}; - -internals.unique.strings = { - dups: ['a', 'b', 'c', 'd', 'a', 'c', 'e'], - result: ['a', 'b', 'c', 'd', 'e'] -}; - -internals.unique.mixed = { - dups: [1, 2, 'a', 'b', internals.unique.item.objects[0], 'a', '2', 3, internals.unique.item.objects[0]], - result: [1, 2, 'a', ',b', internals.unique.item.objects[0], 3] -}; - - describe('clone()', () => { it('clones a nested object', () => { @@ -1215,75 +1183,6 @@ describe('deepEqual()', () => { }); }); -describe('unique()', () => { - - const deprecatedUnique = function (array, key) { // previous method of unique from hapi 3.0.4 - - const index = {}; - const result = []; - - for (let i = 0; i < array.length; ++i) { - const id = (key ? array[i][key] : array[i]); - if (index[id] !== true) { - - result.push(array[i]); - index[id] = true; - } - } - - return result; - }; - - it('ensures uniqueness within array of objects based on subkey', () => { - - expect(Hoek.unique(internals.unique.objectsByKey.dups, 'x')).to.equal(internals.unique.objectsByKey.result); - expect(deprecatedUnique(internals.unique.objectsByKey.dups, 'x')).to.equal(internals.unique.objectsByKey.result); - - }); - - it('removes duplicated integers without key', () => { - - expect(Hoek.unique(internals.unique.integers.dups)).to.equal(internals.unique.integers.result); - expect(deprecatedUnique(internals.unique.integers.dups)).to.equal(internals.unique.integers.result); - }); - - it('removes duplicated strings without key', () => { - - expect(Hoek.unique(internals.unique.strings.dups)).to.equal(internals.unique.strings.result); - expect(deprecatedUnique(internals.unique.strings.dups)).to.equal(internals.unique.strings.result); - }); - - it('removes duplicated objects without key', () => { // this was not supported in earlier versions - - expect(Hoek.unique(internals.unique.objects.dups)).to.equal(internals.unique.objects.result); - expect(deprecatedUnique(internals.unique.objects.dups)).to.not.equal(internals.unique.objects.result); - }); -}); - -describe('mapToObject()', () => { - - it('returns null on null array', () => { - - const a = Hoek.mapToObject(null); - expect(a).to.equal(null); - }); - - it('converts basic array to existential object', () => { - - const keys = [1, 2, 3, 4]; - const a = Hoek.mapToObject(keys); - expect(Object.keys(a)).to.equal(['1', '2', '3', '4']); - }); - - it('converts array of objects to existential object', () => { - - const keys = [{ x: 1 }, { x: 2 }, { x: 3 }, { y: 4 }]; - const subkey = 'x'; - const a = Hoek.mapToObject(keys, subkey); - expect(a).to.equal({ 1: true, 2: true, 3: true }); - }); -}); - describe('intersect()', () => { it('returns the common objects of two arrays', () => { @@ -1314,13 +1213,14 @@ describe('intersect()', () => { expect(Hoek.intersect([1], null).length).to.equal(0); expect(Hoek.intersect(null, [1]).length).to.equal(0); + expect(Hoek.intersect(null, [1], true)).to.be.null(); }); it('returns the common objects of object and array', () => { - const array1 = [1, 2, 3, 4, 4, 5, 5]; + const array1 = { 1: true, 2: true, 3: true, 4: true, 5: true }; const array2 = [5, 4, 5, 6, 7]; - const common = Hoek.intersect(Hoek.mapToObject(array1), array2); + const common = Hoek.intersect(array1, array2); expect(common.length).to.equal(2); }); }); @@ -1821,218 +1721,6 @@ describe('ignore()', () => { }); }); -describe('transform()', () => { - - const source = { - address: { - one: '123 main street', - two: 'PO Box 1234' - }, - zip: { - code: 3321232, - province: null - }, - title: 'Warehouse', - state: 'CA' - }; - - const sourcesArray = [{ - address: { - one: '123 main street', - two: 'PO Box 1234' - }, - zip: { - code: 3321232, - province: null - }, - title: 'Warehouse', - state: 'CA' - }, { - address: { - one: '456 market street', - two: 'PO Box 5678' - }, - zip: { - code: 9876, - province: null - }, - title: 'Garage', - state: 'NY' - }]; - - it('transforms an object based on the input object', () => { - - const result = Hoek.transform(source, { - 'person.address.lineOne': 'address.one', - 'person.address.lineTwo': 'address.two', - 'title': 'title', - 'person.address.region': 'state', - 'person.address.zip': 'zip.code', - 'person.address.location': 'zip.province' - }); - - expect(result).to.equal({ - person: { - address: { - lineOne: '123 main street', - lineTwo: 'PO Box 1234', - region: 'CA', - zip: 3321232, - location: null - } - }, - title: 'Warehouse' - }); - }); - - it('transforms an array of objects based on the input object', () => { - - const result = Hoek.transform(sourcesArray, { - 'person.address.lineOne': 'address.one', - 'person.address.lineTwo': 'address.two', - 'title': 'title', - 'person.address.region': 'state', - 'person.address.zip': 'zip.code', - 'person.address.location': 'zip.province' - }); - - expect(result).to.equal([ - { - person: { - address: { - lineOne: '123 main street', - lineTwo: 'PO Box 1234', - region: 'CA', - zip: 3321232, - location: null - } - }, - title: 'Warehouse' - }, - { - person: { - address: { - lineOne: '456 market street', - lineTwo: 'PO Box 5678', - region: 'NY', - zip: 9876, - location: null - } - }, - title: 'Garage' - } - ]); - }); - - it('uses the reach options passed into it', () => { - - const schema = { - 'person-address-lineOne': 'address-one', - 'person-address-lineTwo': 'address-two', - 'title': 'title', - 'person-address-region': 'state', - 'person-prefix': 'person-title', - 'person-zip': 'zip-code' - }; - const options = { - separator: '-', - default: 'unknown' - }; - const result = Hoek.transform(source, schema, options); - - expect(result).to.equal({ - person: { - address: { - lineOne: '123 main street', - lineTwo: 'PO Box 1234', - region: 'CA' - }, - prefix: 'unknown', - zip: 3321232 - }, - title: 'Warehouse' - }); - }); - - it('uses a default separator for keys if options does not specify on', () => { - - const schema = { - 'person.address.lineOne': 'address.one', - 'person.address.lineTwo': 'address.two', - 'title': 'title', - 'person.address.region': 'state', - 'person.prefix': 'person.title', - 'person.zip': 'zip.code' - }; - const options = { - default: 'unknown' - }; - const result = Hoek.transform(source, schema, options); - - expect(result).to.equal({ - person: { - address: { - lineOne: '123 main street', - lineTwo: 'PO Box 1234', - region: 'CA' - }, - prefix: 'unknown', - zip: 3321232 - }, - title: 'Warehouse' - }); - }); - - it('works to create shallow objects', () => { - - const result = Hoek.transform(source, { - lineOne: 'address.one', - lineTwo: 'address.two', - title: 'title', - region: 'state', - province: 'zip.province' - }); - - expect(result).to.equal({ - lineOne: '123 main street', - lineTwo: 'PO Box 1234', - title: 'Warehouse', - region: 'CA', - province: null - }); - }); - - it('only allows strings in the map', () => { - - expect(() => { - - Hoek.transform(source, { - lineOne: {} - }); - }).to.throw('All mappings must be "." delineated strings'); - }); - - it('throws an error on invalid arguments', () => { - - expect(() => { - - Hoek.transform(NaN, {}); - }).to.throw('Invalid source object: must be null, undefined, an object, or an array'); - }); - - it('is safe to pass null', () => { - - const result = Hoek.transform(null, {}); - expect(result).to.equal({}); - }); - - it('is safe to pass undefined', () => { - - const result = Hoek.transform(undefined, {}); - expect(result).to.equal({}); - }); -}); - describe('uniqueFilename()', () => { it('generates a random file path', () => { @@ -2094,43 +1782,6 @@ describe('stringify()', () => { }); }); -describe('shallow()', () => { - - it('shallow copies an object', () => { - - const obj = { - a: 5, - b: { - c: 6 - } - }; - - const shallow = Hoek.shallow(obj); - expect(shallow).to.not.shallow.equal(obj); - expect(shallow).to.equal(obj); - expect(shallow.b).to.equal(obj.b); - }); - - it('copies properties from a function', () => { - - const fn = function () { }; - fn.a = 5; - fn.b = { c: 6 }; - - const shallow = Hoek.shallow(fn); - expect(shallow).to.be.an.object(); - expect(shallow).to.not.shallow.equal(fn); - expect(Object.entries(shallow)).to.equal(Object.entries(fn)); - expect(shallow.b).to.equal(fn.b); - }); - - it('returns empty object for null and undefined', () => { - - expect(Hoek.shallow(null)).to.equal({}); - expect(Hoek.shallow(undefined)).to.equal({}); - }); -}); - describe('block()', () => { it('executes ignore function', async () => {