Skip to content

Instantly share code, notes, and snippets.

@petsel
Last active February 22, 2021 17:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petsel/32d7d06a205086c413c3 to your computer and use it in GitHub Desktop.
Save petsel/32d7d06a205086c413c3 to your computer and use it in GitHub Desktop.
Implementation of a condition-function based, prototypal `Array` method which returns rejected array items and does mutate its processed array.
/**
*
* [Array.reject.js]
*
* Implementation of a condition-function based, prototypal `Array` method
* which returns rejected array items and does mutate its processed array.
*
* - `Array.prototype.reject`
*
*/
(function (ReferenceError, TypeError, Object, Set, Array) {
const arrPrototype = Array.prototype;
const { isArray } = Array;
const FUNCTION_TYPE = (typeof Object);
function isFunction(type) {
/* eslint-disable valid-typeof */
return (
(typeof type === FUNCTION_TYPE)
&& (typeof type.call === FUNCTION_TYPE)
&& (typeof type.apply === FUNCTION_TYPE)
);
/* eslint-enable valid-typeof */
}
function getSanitizedTarget(target) {
/* eslint-disable */
/* jshint ignore:start */
/* ignore jslint start */
/* no-eq-null */
return ((target != null) && target) || null;
/* ignore jslint end */
/* jshint ignore:end */
/* eslint-enable */
}
function collectSpliceRange(rangeList, currentSplicePosition, idx, splicePositionList) {
let range;
const recentSplicePosition = splicePositionList[idx - 1] ?? null;
const nextSplicePosition = splicePositionList[idx + 1] ?? null;
const isOpenNewRange = (
(recentSplicePosition === null) ||
(currentSplicePosition - recentSplicePosition !== 1)
);
const isTerminateRange = (recentSplicePosition !== null) && (
(nextSplicePosition === null) ||
(nextSplicePosition - currentSplicePosition !== 1)
);
if (isOpenNewRange) {
range = [ currentSplicePosition ];
rangeList.push(range);
}
if (isTerminateRange) {
range = rangeList[rangeList.length - 1];
range.push(currentSplicePosition);
}
return rangeList;
}
function createSpliceRangeList(splicePositionList) {
return splicePositionList
// create list of splice ranges.
.reduce(collectSpliceRange, [])
// in order to be able to splice/delete
// items from each target array's right side.
.reverse();
}
function collectTargetItemsBySpliceRange(collector, [idxStart, idxEnd]) {
// keep filling the list of rejected items FROM its LEFT side while mutating the target array.
collector.rejected.unshift(...collector.target.splice(idxStart, (idxEnd - idxStart + 1)));
return collector;
}
function rejectTargetItemsBySpliceRangeList(rangeList, target) {
return rangeList
// collect the list of rejected items by mutating the target array.
.reduce(collectTargetItemsBySpliceRange, { target, rejected: [] }).rejected;
}
function rejectEveryMatchingItemByCondition(arr, condition, target) {
const list = [];
let idx = arr.length;
const copy = [...arr];
// Processing the array from RIGHT to LEFT keeps the `idx` always in sync
// with both related array items, the one of the mutated and also the one
// of the unmutated version of the processed array reference.
// Thus the `condition` always gets passed the unmutated shallow copy.
while (idx) {
// eslint-disable-next-line no-plusplus,no-prototype-builtins
if (arr.hasOwnProperty(--idx)) { // take *sparse array* into account.
// arguments list ... [elm, idx, arr] ... called within `target` context.
if (condition.call(target, copy[idx], idx, copy)) { // keep processing the unmutated array by the condition method.
// keep filling the list of rejected items FROM its LEFT side while mutating the target array.
list.unshift(arr.splice(idx, 1)[0]);
}
}
}
// returns an array of rejected items but not the processed and mutated array reference.
return list;
}
function rejectEveryMatchingItemByConditionAndSpliceList(arr, condition, target) {
const splicePositionList = arr.reduce((list, item, idx, reference) => {
if (condition.call(target, item, idx, reference)) {
list.push(idx);
}
return list;
}, []).sort((a, b) => a - b); // always assure ascending numerical order of indices.
// returns an array of rejected items but not the processed and mutated array reference.
return rejectTargetItemsBySpliceRangeList(
// always assure a list/set of unique indices.
[...new Set(createSpliceRangeList(splicePositionList))],
arr
);
}
function reject(condition, target) {
if (isArray(this)) {
if (isFunction(condition)) {
if (this.length > 100) { // an arbitrarily chosen threshold value.
// optimized splicing process from right to left by first running a task which maps slice ranges.
return rejectEveryMatchingItemByConditionAndSpliceList(this, condition, getSanitizedTarget(target));
}
// efficient enough splicing process from right to left at direct condition matches.
return rejectEveryMatchingItemByCondition(this, condition, getSanitizedTarget(target));
}
throw (new TypeError('"reject" needs to be provided a `Function` type condition to.'));
} else {
throw (new ReferenceError('"reject" has to be processed within an `Array` type context.'));
}
}
Object.defineProperty(arrPrototype, 'reject', {
configurable: true,
writable: true,
value: reject
});
Object.defineProperty(arrPrototype.reject, 'toString', {
value: () => 'function reject() { [custom code] }'
});
// // provide static implementation as well.
//
// function staticReject(arr, condition, target) {
// return reject.call(arr, condition, target);
// }
//
// Object.defineProperty(Array, 'reject', {
// configurable: true,
// writable: true,
// value: staticReject
// });
// Object.defineProperty(Array.reject, 'toString', {
// value: () => 'function reject() { [custom code] }'
// });
// return Array;
}(ReferenceError, TypeError, Object, Set, Array));
// function condition(elm) {
// return (elm >= 6);
// }
// const arr = [9, 0, 8, 1, 7, 2, 6, 3, 5, 4];
//
// console.log('arr.reject(condition), arr;', arr.reject(condition), arr);
//
// console.log('[].reject();', [].reject());
// console.log('[].reject(function(){});', [].reject(function(){}));
//
// console.log('Array.prototype.reject.call();', Array.prototype.reject.call());
// console.log('Array.prototype.reject.call(null);', Array.prototype.reject.call(null));
//
// console.log('Array.prototype.reject.call([]);', Array.prototype.reject.call([]));
// console.log('Array.prototype.reject.call([], function(){});', Array.prototype.reject.call([], function(){}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment