diff --git a/lib/utils.js b/lib/utils.js index 75ca6f8b..e6bfd32d 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -30,51 +30,37 @@ exports.merge = function (target, source, options) { if (typeof source !== 'object') { if (Array.isArray(target)) { target.push(source); + return target; } else if (typeof target === 'object') { - if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) { - target[source] = true; - } + source = exports.arrayToObject([source], options); } else { - return [target, source]; + target = [target, source]; + return target; } - - return target; - } - - if (typeof target !== 'object') { - return [target].concat(source); - } - - var mergeTarget = target; - if (Array.isArray(target) && !Array.isArray(source)) { - mergeTarget = exports.arrayToObject(target, options); - } - - if (Array.isArray(target) && Array.isArray(source)) { - source.forEach(function (item, i) { - if (has.call(target, i)) { - if (target[i] && typeof target[i] === 'object') { - target[i] = exports.merge(target[i], item, options); - } else { - target.push(item); - } - } else { - target[i] = item; - } - }); + } else if (Array.isArray(target) && + !Array.isArray(source)) { + target = exports.arrayToObject(target, options); + } else if (Array.isArray(source) && typeof target !== 'object') { + source.splice(0, 0, target); + target = source; return target; + } else if (typeof target !== 'object') { + target = exports.arrayToObject([target], options); } - return Object.keys(source).reduce(function (acc, key) { + var keys = Object.keys(source); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; var value = source[key]; - if (has.call(acc, key)) { - acc[key] = exports.merge(acc[key], value, options); + if (has.call(target, key)) { + target[key] = exports.merge(target[key], value, options); } else { - acc[key] = value; + target[key] = value; } - return acc; - }, mergeTarget); + } + + return target; }; exports.assign = function assignSingleSource(target, source) { diff --git a/test/parse.js b/test/parse.js index a92ea15c..1e5e726a 100644 --- a/test/parse.js +++ b/test/parse.js @@ -535,5 +535,80 @@ test('parse()', function (t) { st.end(); }); + t.test('parses string/object combination', function (st) { + + var expectObj = { a: { 0: 'val1', b: 'val2' } }; + st.deepEqual(qs.parse('a=val1&a.b=val2', { allowDots: true }), expectObj); + st.deepEqual(qs.parse('a.b=val2&a=val1', { allowDots: true }), expectObj); + expectObj = { a: { 0: 'val1', b: 'val2', c: 'val3' } }; + st.deepEqual(qs.parse('a.b=val2&a=val1&a.c=val3', { allowDots: true }), expectObj); + expectObj = { a: { 0: 'val1', 1: 'val3', b: 'val2' } }; + st.deepEqual(qs.parse('a.b=val2&a=val1&a=val3', { allowDots: true }), expectObj); + st.deepEqual(qs.parse('a=val1&a.b=val2&a=val3', { allowDots: true }), expectObj); + expectObj = { a: { 0: 'val1', 1: 'val2', 2: 'val3', b: 'val4' } }; + st.deepEqual(qs.parse('a=val1&a=val2&a=val3&a.b=val4', { allowDots: true }), expectObj); + expectObj = { a: { 0: 'val1', b: ['val2', 'val3'] } }; + st.deepEqual(qs.parse('a=val1&a.b=val2&a.b=val3', { allowDots: true }), expectObj); + expectObj = { a: { 0: 'val1', b: { 0: 'val2', c: 'val3' } } }; + st.deepEqual(qs.parse('a=val1&a.b=val2&a.b.c=val3', { allowDots: true }), expectObj); + st.deepEqual(qs.parse('a.b.c=val3&a.b=val2&a=val1', { allowDots: true }), expectObj); + st.end(); + }); + + t.test('parses string/object combination', function (st) { + st.deepEqual( + qs.parse('a=val1&a.b=val2', { allowDots: true }), + { a: { 0: 'val1', b: 'val2' } }, + 'string + object' + ); + st.deepEqual( + qs.parse('a.b=val2&a=val1', { allowDots: true }), + { a: { 0: 'val1', b: 'val2' } }, + 'object + string' + ); + + st.deepEqual( + qs.parse('a.b=val2&a=val1&a.c=val3', { allowDots: true }), + { a: { 0: 'val1', b: 'val2', c: 'val3' } }, + 'object + string + object' + ); + + st.deepEqual( + qs.parse('a.b=val2&a=val1&a=val3', { allowDots: true }), + { a: { 0: 'val1', 1: 'val3', b: 'val2' } }, + 'object + string + string' + ); + st.deepEqual( + qs.parse('a=val1&a.b=val2&a=val3', { allowDots: true }), + { a: { 0: 'val1', 1: 'val3', b: 'val2' } }, + 'string + object + string' + ); + + st.deepEqual( + qs.parse('a=val1&a=val2&a=val3&a.b=val4', { allowDots: true }), + { a: { 0: 'val1', 1: 'val2', 2: 'val3', b: 'val4' } }, + 'string + string + string + object' + ); + + st.deepEqual( + qs.parse('a=val1&a.b=val2&a.b=val3', { allowDots: true }), + { a: { 0: 'val1', b: ['val2', 'val3'] } }, + 'string + object + object' + ); + + st.deepEqual( + qs.parse('a=val1&a.b=val2&a.b.c=val3', { allowDots: true }), + { a: { 0: 'val1', b: { 0: 'val2', c: 'val3' } } }, + 'string + object + nested object' + ); + st.deepEqual( + qs.parse('a.b.c=val3&a.b=val2&a=val1', { allowDots: true }), + { a: { 0: 'val1', b: { 0: 'val2', c: 'val3' } } }, + 'nested object + object + string' + ); + + st.end(); + }); + t.end(); });