Last active
February 22, 2021 17:25
-
-
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.
This file contains hidden or 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
/** | |
* | |
* [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