diff --git a/README.md b/README.md index 2e4cb3c..5368914 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Use a custom delimiter for (un)flattening your objects, instead of `.`. When enabled, both `flat` and `unflatten` will preserve arrays and their contents. This is disabled by default. + ``` javascript var flatten = require('flat') @@ -158,3 +159,14 @@ flatten({ // 'key3.a': { b: { c: 2 } } // } ``` +### shallow + +When enabled, nested flattened objects are preserved when unflattening. + +``` +unflatten({ "foo.bar": { "fiz.fuz": "hello" }}) +// { foo: { bar: { "fiz": { "fuz": "hello" } } } + +unflatten({ "foo.bar": { "fiz.fuz": "hello" }}, { shallow: true }) +// { foo: { bar: { "fiz.fuz": "hello" } } +``` diff --git a/index.js b/index.js index 4d6914a..932cb4b 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,8 @@ function flatten(target, opts) { var currentDepth = 1 var output = {} + var changed = false + function step(object, prev) { Object.keys(object).forEach(function(key) { var value = object[key] @@ -34,19 +36,28 @@ function flatten(target, opts) { return step(value, newKey) } + if (newKey !== key) { + changed = true + } + output[newKey] = value }) } step(target) - return output + if (changed) { + return output + } else { + return target + } } function unflatten(target, opts) { opts = opts || {} var delimiter = opts.delimiter || '.' + var shallow = opts.shallow || false var overwrite = opts.overwrite || false var result = {} @@ -55,6 +66,8 @@ function unflatten(target, opts) { return target } + var changed = false + // safely ensure that the key is // an integer. function getkey(key) { @@ -74,6 +87,8 @@ function unflatten(target, opts) { var recipient = result while (key2 !== undefined) { + changed = true + var type = Object.prototype.toString.call(recipient[key1]) var isobject = ( type === "[object Object]" || @@ -94,11 +109,21 @@ function unflatten(target, opts) { } } - // unflatten again for 'messy objects' - recipient[key1] = unflatten(target[key], opts) + if (!shallow) { + // unflatten again for 'messy objects' + var value = target[key] + recipient[key1] = unflatten(target[key], opts) + changed = changed || value !== recipient[key1] + } else { + recipient[key1] = target[key] + } }) - return result + if (changed) { + return result + } else { + return target + } } function isBuffer(value) { diff --git a/test/test.js b/test/test.js index 707901b..65e7fd9 100644 --- a/test/test.js +++ b/test/test.js @@ -12,6 +12,20 @@ var primitives = { , undefined: undefined } +test('Isomorphism', function() { + var original = { foo: 'bar' } + + assert.strictEqual( + original, + flatten(unflatten(original)) + ) + + assert.strictEqual( + original, + unflatten(flatten(original)) + ) +}); + suite('Flatten Primitives', function() { Object.keys(primitives).forEach(function(key) { var value = primitives[key] @@ -115,6 +129,12 @@ suite('Flatten', function() { }) }) + test('Identity', function() { + var object = { foo: 'baz', fiz: 'fuz' } + + assert.strictEqual(flatten(object), flatten(object)) + }) + if (typeof Buffer !== 'undefined') test('Buffer', function() { assert.deepEqual(flatten({ hello: { @@ -263,6 +283,65 @@ suite('Unflatten', function() { })) }) + test('Identity', function() { + var object = { foo: { bar: 'baz' } } + + assert.strictEqual(unflatten(object), unflatten(object)) + }); + + suite('.shallow', function() { + test('Should leave nested objects untouched', function() { + assert.deepEqual(unflatten({ + 'hello.world': { 'foo.fiz': 'bar' } + }, { shallow: true }), { + 'hello': { + 'world': { + 'foo.fiz': 'bar' + } + } + }) + }); + + test('Should preserve object identity', function() { + var object = { + 'hello.world': { foo: 'bar' } + } + + var unflattened1 = unflatten(object, { shallow: true }) + var unflattened2 = unflatten(object, { shallow: true }) + + assert.deepEqual(unflattened1.hello.world, { foo: 'bar' }) + assert.strictEqual(unflattened1.hello.world, unflattened2.hello.world) + }) + + test('Identity', function() { + var object = { foo: { "ir.re.le.vant": 'baz' } } + + assert.strictEqual( + unflatten(object, { shallow: true }), + unflatten(object, { shallow: true }) + ) + }); + + test('Isomorphism 1', function() { + var object = { foo: { "ir.re.le.vant": 'baz' } } + + assert.strictEqual( + object, + flatten(unflatten(object, { shallow: true }), { maxDepth: 1 }) + ) + }); + + test('Isomorphism 2', function() { + var object = { foo: { "ir.re.le.vant": 'baz' } } + + assert.strictEqual( + object, + unflatten(flatten(object, { maxDepth: 1 }), { shallow: true }) + ) + }); + }) + suite('.safe', function() { test('Should protect arrays when true', function() { assert.deepEqual(flatten({