Skip to content

Instantly share code, notes, and snippets.

@abrahamjuliot
Last active February 25, 2017 02:36
Show Gist options
  • Save abrahamjuliot/e13d3a90cf09ed561af3b1ff48db18e4 to your computer and use it in GitHub Desktop.
Save abrahamjuliot/e13d3a90cf09ed561af3b1ff48db18e4 to your computer and use it in GitHub Desktop.
filter function for js arrays and or array of objects
// filterBy(deepArrayOfAnything, "friend", {limitTo: 2, sortBy: "name", ignoreKey: "keywords"})
(function (win, doc, undefined) {
const isTypeArray = (obj) => Array.isArray(obj),
isTypeObject = (obj) => (typeof obj === 'object'),
isObjectOrArray = (obj) => (isTypeObject(obj) || isTypeArray(obj)),
isObjectAndNotArray = (obj) => (isTypeObject(obj) && !isTypeArray(obj)),
isNumeric = (num) => !isNaN(num),
isNumberBooleanOrString = (obj) => (
typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean'),
stringVersionHas = (str, search) => str.toString().toLowerCase().indexOf(search.toLowerCase()) > -1,
listOrChildListHasObject = (arr) => {
for (let item of arr) {
if (isTypeArray(item) && listOrChildListHasObject(item)) {return true;} // deep search arrays
else if (isTypeObject(item)) {return true;}
}
return false;
},
filterArray = (arr, val) => arr.filter((item) => stringVersionHas(item, val)),
limitTo = (arr, limit) => arr.slice(0, limit),
by = (getValue) => (a, b) => getValue(a) > getValue(b) ? 1 : -1,
sortBy = (arr, val) => arr.sort(by(obj => obj[val]));
win.filterBy = (list, query, options = null) => {
// throw type error if list is not an array
if (!isTypeArray(list)) {throw new TypeError('first argument must be of type Array');}
// throw type error if qury is not an number, boolean, or string
else if (!isNumberBooleanOrString(query)) {
throw new TypeError('second argument must be of type String, Boolean, or Number');
}
// throw type error if set options is not an object
else if (options !== null && !isObjectAndNotArray(options)) {
throw new TypeError('third argument must be of type Object');
}
// return list if it is empty
else if (!list.length) {return list;}
// begin by converting query to string and triming it
else {query = query.toString().trim();}
// query the list and return item found if it or any child lists do not have an object
if (!listOrChildListHasObject(list)) {return filterArray(list, query);}
// Otherwise, since there are objects
let collection = [], // to hold item object(s) containing query
ignoreKeyIsOn = (options !== null && typeof options.ignoreKey === 'string');
// for each parent item
for (let parentIndex = 0, length = list.length; parentIndex < length; parentIndex++) {
let parentItem = list[parentIndex], keys = [];
if (isNumberBooleanOrString(parentItem) && stringVersionHas(parentItem, query)) {
collection.push(parentItem);
continue; // to next parent item
} else {
keys = Object.keys(parentItem);
}
// for each child item
for (let i = 0, childItem = {}, len = keys.length; i < len; i++) {
let childItem = parentItem[keys[i]];
if (isObjectAndNotArray(childItem) && filterBy([childItem], query).length) {
if (ignoreKeyIsOn && keys[i] == options.ignoreKey) break;
collection.push(parentItem);
break;
} else if (isTypeArray(childItem) && filterBy(childItem, query).length) {
if (ignoreKeyIsOn && keys[i] == options.ignoreKey) break;
collection.push(parentItem);
break;
} else if (stringVersionHas(childItem, query)) {
if (ignoreKeyIsOn && keys[i] == options.ignoreKey) break;
collection.push(parentItem);
break;
}
}
}
// check for options
if (options !== null) {
if (isNumeric(options.limitTo) && typeof options.sortBy === 'string') {
return limitTo(sortBy(collection, options.sortBy), parseInt(options.limitTo, 10));
}
else if (isNumeric(options.limitTo)) {
return limitTo(collection, parseInt(options.limitTo, 10));
}
else if (typeof options.sortBy === 'string') {
return sortBy(collection, options.sortBy);
}
}
else {return collection;}
};
})(window, document);
// test at https://repl.it/EyQA/53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment