Created
September 20, 2016 13:14
-
-
Save turboza/d30675f1a309e6e8799f243dcf9c170c to your computer and use it in GitHub Desktop.
Parameter serializer to create URL query string from nested objects.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ========================================= | |
* paramSerialize | |
* ========================================= | |
* Serialize JS object into URL Query string format. | |
* | |
* @param {object} params - { a: 10, filters: { x: 2, y: true } } | |
* @return {string} - '?a=10&filters[x]=2&filters[y]=true' | |
*/ | |
export default function paramSerialize(params) { | |
if (! isObject(params)) { | |
console.warn(`params (${params}) must be an object`); | |
return ''; | |
} | |
const queryArray = objectToQueryList(params); | |
return queryArray.length > 0 ? ('?' + queryArray.join('&')) : ''; | |
} | |
/** | |
* Helper - Object to QueryString array | |
* ----------------------------------------- | |
* | |
* Usage: | |
* objectToQueryList({ a: 10, b: 'hi' }) === ['a=10', 'b=hi'] | |
* objectToQueryList({ a: 10, filters: { x: 2, y: true } }) === ['a:10', 'filters[x]=2', 'filters[y]=true'] | |
* objectToQueryList({ filters: { extra: { m: 5 } } }) === ['filters[extra][m]=5'] | |
*/ | |
function objectToQueryList(object, parentKey) { | |
return Object.keys(object).reduce((prevList, key) => { | |
const value = object[key]; | |
if (isInvalidValue(value)) { | |
return prevList; | |
} | |
const encodedKey = encodeURIComponent(key); | |
const fullKey = parentKey ? `${parentKey}[${encodedKey}]` : encodedKey; | |
if (isObjectOrArray(value)) { | |
return prevList.concat(objectToQueryList(value, fullKey)); | |
} | |
const encodedValue = encodeURIComponent(value); | |
return prevList.concat(`${fullKey}=${encodedValue}`); | |
}, []); | |
} | |
/** | |
* Helper - isObject (Not Array) | |
* ----------------------------------------- | |
*/ | |
function isObject(input) { | |
return input && typeof(input) === 'object' && ! Array.isArray(input); | |
} | |
/** | |
* Helper - isInvalidValue | |
* ----------------------------------------- | |
*/ | |
function isInvalidValue(input) { | |
return typeof(input) === 'undefined' || input === null || input === ''; | |
} | |
/** | |
* Helper - isObjectOrArray | |
* ----------------------------------------- | |
*/ | |
function isObjectOrArray(input) { | |
return input && typeof(input) === 'object'; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import expect from 'expect'; | |
import paramSerialize from 'src/helpers/paramSerialize.js'; | |
describe('paramSerialize()', () => { | |
it('should return empty string for empty object', () => { | |
expect(paramSerialize({})).toEqual(''); | |
}); | |
it('should return empty string for invalid object', () => { | |
expect(paramSerialize('')).toEqual(''); | |
expect(paramSerialize(1)).toEqual(''); | |
expect(paramSerialize(1.123)).toEqual(''); | |
expect(paramSerialize(null)).toEqual(''); | |
expect(paramSerialize(true)).toEqual(''); | |
expect(paramSerialize(false)).toEqual(''); | |
expect(paramSerialize('test string')).toEqual(''); | |
expect(paramSerialize(['abc', 123])).toEqual(''); | |
expect(paramSerialize()).toEqual(''); | |
}); | |
it('should ignore undefine, null, blank, property', () => { | |
expect(paramSerialize({ limit: 20, offset: undefined })).toEqual('?limit=20'); | |
expect(paramSerialize({ limit: 20, offset: null })).toEqual('?limit=20'); | |
expect(paramSerialize({ limit: 20, offset: '' })).toEqual('?limit=20'); | |
}); | |
it('should return correct serialized string with edge cases, 0 or false', () => { | |
expect(paramSerialize({ a: false })).toEqual('?a=false'); | |
expect(paramSerialize({ a: 0 })).toEqual('?a=0'); | |
}); | |
it('should return correct serialized string', () => { | |
expect(paramSerialize({ a: 1, b: 'hi' })).toEqual('?a=1&b=hi'); | |
expect(paramSerialize({ a: 1, b: true })).toEqual('?a=1&b=true'); | |
}); | |
it('should encode string', () => { | |
expect(paramSerialize({ a:1, 'b boy':'hi nam' })).toEqual('?a=1&b%20boy=hi%20nam'); | |
}); | |
it('should encode 1 level of nested object', () => { | |
const params = { a: 1, filters: { amount: '1000..5000', paid: '1' } }; | |
expect(paramSerialize(params)) | |
.toEqual('?a=1&filters[amount]=1000..5000&filters[paid]=1'); | |
}); | |
it('should encode 2 levels of nested object', () => { | |
const params = { | |
a: 1, | |
filters: { | |
amount: '1000..5000', paid: '1', extra: { iin: '424242' }, | |
}, | |
}; | |
expect(paramSerialize(params)) | |
.toEqual('?a=1&filters[amount]=1000..5000&filters[paid]=1&filters[extra][iin]=424242'); | |
}); | |
it('should ignore empty object', () => { | |
expect(paramSerialize({ a: 10, filters: {} })).toEqual('?a=10'); | |
expect(paramSerialize({ a: 10, filters: { b: 20, extra: {} } })) | |
.toEqual('?a=10&filters[b]=20'); | |
}); | |
it('should be able to handle array inside', () => { | |
expect(paramSerialize({ a: 10, filters: ['hi', 1] })) | |
.toEqual('?a=10&filters[0]=hi&filters[1]=1'); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment