Skip to content

Instantly share code, notes, and snippets.

@john-yuan
Last active January 12, 2019 09:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save john-yuan/fac5c91a6f7b6b6dea4e73f00f5636e2 to your computer and use it in GitHub Desktop.
Save john-yuan/fac5c91a6f7b6b6dea4e73f00f5636e2 to your computer and use it in GitHub Desktop.
An util to encode object to query string or decode query string to object.
/**
* An util to encode object to query string or decode query string to object.
*
* API:
*
* * `QS.encode(object: Object.<string, *>, keepArrayIndex?: boolean) => string`
* * `QS.decode(string: string) => Object.<string, *>`
*
* @author John Yuan <https://github.com/john-yuan>
* @see {@link https://gist.github.com/john-yuan/fac5c91a6f7b6b6dea4e73f00f5636e2}
*/
var QS = (function () {
// namespace
var QS = {};
// caches
var toString = Object.prototype.toString;
var hasOwn = Object.prototype.hasOwnProperty;
/**
* Encode the given object to URI Component encoded query string
*
* @param {Object.<string, *>} object The object to encode
* @param {boolean} [keepArrayIndex] Wether to keep array index
* @returns {string} Returns the URI Component encoded query string
*/
QS.encode = function (object, keepArrayIndex) {
var key;
var keyValueArray = [];
keepArrayIndex = !!keepArrayIndex;
if ( isObject(object) ) {
for ( key in object ) {
if ( hasOwn.call(object, key) ) {
encodeKey(key, object[key], keyValueArray, keepArrayIndex);
}
}
}
return keyValueArray.join('&');
};
/**
* Decode the URI Component encoded query string to object
*
* @param {string} The URI Component encoded query string
* @returns {Object.<string, string>} Returns the decoded object
*/
QS.decode = function (string) {
var object = {};
var cache = {};
var keyValueArray;
var index;
var length;
var keyValue;
var key;
var value;
// do not decode empty string or something that is not string
if (string && typeof string === 'string') {
keyValueArray = string.split('&');
index = 0;
length = keyValueArray.length;
while (index < length) {
keyValue = keyValueArray[index].split('=');
key = decodeURIComponent(keyValue[0]);
value = keyValue[1];
if (value) {
value = decodeURIComponent(value);
} else {
value = '';
}
decodeKey(object, cache, key, value);
index += 1;
}
}
return object;
};
/**
* Encode the speceifed key in the object
*
* @param {string} key The key name
* @param {any} data The data of the key
* @param {string[]} keyValueArray The array to store the key value string
* @param {boolean} keepArrayIndex Wether to keep array index
*/
var encodeKey = function (key, data, keyValueArray, keepArrayIndex) {
var prop;
var index;
var length;
var value;
var subKey;
if ( isObject(data) ) {
for ( prop in data ) {
if ( hasOwn.call(data, prop) ) {
value = data[prop];
subKey = key + '[' + prop + ']';
encodeKey(subKey, value, keyValueArray, keepArrayIndex);
}
}
} else if ( isArray(data) ) {
index = 0;
length = data.length;
while (index < length) {
value = data[index];
if ( keepArrayIndex || isArray(value) || isObject(value) ) {
subKey = key + '[' + index + ']';
} else {
subKey = key + '[]';
}
encodeKey(subKey, value, keyValueArray, keepArrayIndex);
index += 1;
}
} else {
// if data is null or undefined, treat it as empty string
if (data === null || data === undefined) {
data = '';
// make sure that data is string if it is not object or array
} else if (typeof data !== 'string') {
data = '' + data;
}
key = encodeURIComponent(key);
data = encodeURIComponent(data);
value = key + '=' + data;
keyValueArray.push(value);
}
};
/**
* Decode the specefied key
*
* @param {Object.<string, string>} object The object to hold the decoded data
* @param {Object.<string, *>} cache The object to hold cache data
* @param {string} key The key name to decode
* @param {any} value The value to decode
*/
var decodeKey = function (object, cache, key, value) {
var rBracket = /\[([^\[]*?)?\]$/;
var rIndex = /(^0$)|(^[1-9]\d*$)/;
var indexOrKeyOrEmpty;
var parentKey;
var arrayOrObject;
var keyIsIndex;
var keyIsEmpty;
var valueIsInArray;
var dataArray;
var length;
// check wether key is something like `person[name]` or `colors[]` or
// `colors[1]`
if ( rBracket.test(key) ) {
indexOrKeyOrEmpty = RegExp.$1;
parentKey = key.replace(rBracket, '');
arrayOrObject = cache[parentKey];
keyIsIndex = rIndex.test(indexOrKeyOrEmpty);
keyIsEmpty = indexOrKeyOrEmpty === '';
valueIsInArray = keyIsIndex || keyIsEmpty;
if (arrayOrObject) {
// convert the array to object
if ( (! valueIsInArray) && isArray(arrayOrObject) ) {
dataArray = arrayOrObject;
length = dataArray.length;
arrayOrObject = {};
while (length--) {
if (arrayOrObject[length] !== undefined) {
arrayOrObject[length] = dataArray[length];
}
}
}
} else {
arrayOrObject = valueIsInArray ? [] : {};
}
if ( keyIsEmpty && isArray(arrayOrObject) ) {
arrayOrObject.push(value);
} else {
// arrayOrObject is array or object here
arrayOrObject[indexOrKeyOrEmpty] = value;
}
cache[parentKey] = arrayOrObject;
decodeKey(object, cache, parentKey, arrayOrObject);
} else {
object[key] = value;
}
};
/**
* Check wether the variable is an object
*
* @param {any} it The variable to check
* @returns {boolean} Returns `true` if it is an object
*/
var isObject = function (it) {
return '[object Object]' === toString.call(it);
};
/**
* Check wether the variable is an array
*
* @param {any} it The variable to check
* @returns {boolean} Returns `true` if it is an array
*/
var isArray = function (it) {
return '[object Array]' === toString.call(it);
};
// expose the API
return QS;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment