No need to extend the native Array prototype to get this done. There are a few methods for dealing with Array manipulation that are already available natively to JavaScript and can tackle your problem. More so, following along with using a custom Array method in conjunction with a for loop for a solution could very easily produce unexpected/unintended negative side effects that limit how well this solution could scale for future use within your application. The most obvious potential pitfall is mutating your original data structures, drastically limiting reusability and making it difficult to easily and accurately debug issues caused further down the application stack. These native Array methods will keep your original data structures intact, creating shallow copies of the Array instances during manipulation and returning them as your final result. As an additional bonus, these methods can be chained together in as many ways and as many times as you may need.
.map()
.filter()
.reduce()
Another Array manipulation method commonly utilized is .forEach(). The .forEach() method removes the hassle of having to write and maintain messy for loops; making the overall codebase much easier to read and much easier to follow logically. However, .forEach(), unlike the 3 previously listed Array manipulation methods, does NOT protect against mutating any original data structures used. It can be included in the chain you may create using the other Array methods mentioned. However, once called, .forEach() breaks the ability to chain your methods any further.
.map(), .filter(), and .reduce() provide a pure, functional approach to the iteration and manipulation of an Array. While all 3 methods still offer a means by which to loop over an Array, each method offers it's own unique functionality and purpose for manipulation. Understanding the distinct use of each method first is crucial to understanding when and how to chain them together effectively.
.MAP()
.map() : Similar to .forEach(), this method loops over each item in the given Array. As the loop iterates over each item's index, the individual items are run against a provided method for manipulation. The end result is a new Array with every item being the product of the transformation function provided during the loop. The important note to remember using .map() is that the new Array that is returned after the loop completes will ALWAYS be the same length as the original Array used. This makes .map() an excellent choice when you need to return a new Array with item values that reflect a desired transformation when compared against the original Array, and all of it's item values.
Example:
const originalArr = [1, 2, 3, 4, 5]
const incrementedArr =
originalArr.map(
(n,i,arr) => {
return n + 4
}
)
console.log(originalArr) // console output => [1, 2, 3, 4, 5]
console.log(incrementedArr) // console output => [5, 6, 7, 8, 9]
In the example above, the incrementedArr constant could be rewritten in a much more concise way. For the purpose of answering your question thoroughly, I wrote it verbosely to illustrate an important note to always keep in mind. .map(), .filter(), and .reduce() each offer three arguments by default. The 3 arguments for .map() and .filter() are the same. They are:
item
The individual item value found at each index of the loop's iteration
index
The index value for each item found during the iteration of the loop
array
Provides a reference to the original array being used by the loop
That said, here's a more concise illustration of .map() from the example above:
const originalArr = [1, 2, 3, 4, 5]
const incrementedArr = originalArr.map(n => n + 4)
console.log(originalArr) // console output => [1, 2, 3, 4, 5]
console.log(incrementedArr) // console output => [5, 6, 7, 8, 9]
.FILTER()
.filter() : Again, like .forEach(), this method loops over each item in the given Array. As it does, it compares the value of each item in the Array against a provided conditional check, returning either true or false for each item at each index within the Array. The final output will be an Array of item's whose values all returned as the same Boolean, either as true or false depending on the conditional check provided. This makes .filter() the perfect choice for when you need to return a new Array that may need to be a different length compared to the original Array provided. In other words, the new Array returned is a direct reflection of the conditional check used to filter out item values no longer needed in the original Array.
Example:
const originalArr = [1, 2, 3, 4, 5]
const noEvenNumbersArr = originalArr.filter(n => n%2)
console.log(originalArr) // console output => [1, 2, 3, 4, 5]
console.log(noEvenNumbersArr) // console output => [1, 3, 5]
.REDUCE()
The .reduce() method, like the other Array methods mentioned, loops through all of it's items in a given Array. It's argument parameters are different than .map() and .filter(). Also, while .map() and .filter() ALWAYS return an Array, .reduce() offers the ability to return and Object instead. .reduce() takes a bit more time to properly explain than the other two methods discussed. Also, using what we've discussed using .map() and .filter(), a solution can be formed to properly address the problem originally posed.
THE SOULTION
// The Array "items" contains values that need to be removed.
const items = [
{ title: 'Bokningsbar',
start: moment("2018-04-05 06:00"),
end: moment("2018-04-05 07:00"),
allDay: false },
{ title: 'Bokningsbar',
start: moment("2018-04-05 06:00"),
end: moment("2018-04-05 07:00"),
allDay: false },
{ title: 'Bokningsbar',
start: moment("2018-04-05 06:00"),
end: moment("2018-04-05 07:00"),
allDay: false },
{ title: 'Bokningsbar',
start: moment("2018-04-05 06:00"),
end: moment("2018-04-05 07:00"),
allDay: false }
]
// The Array "datesToRemove" contains values that match what needs
// to be removed from the"items" Array
const datesToRemove = [
{ title: 'Bokningsbar',
start: moment("2018-04-05 06:00"),
end: moment("2018-04-05 07:00"),
allDay: false }
]
The .filter() method can handle everything needed for a solution to the problem... Almost. The problem now revolves around 2 things specifically,
1. Not filtering an Array of items against another Array of items. Instead, you're trying to filter an Array of OBJECTS against another Array of OBJECTS. If that is the intention here, you can modify the following to check for native Object methods (and iterators) such as Object.keys() and Object.values().
const mergeUniqueMatches = (catArr) => catArr.filter((elem, pos, arr) => arr.indexOf(elem) == pos)
const mergedArr = mergeUniqueMatches(items.concat(datesToRemove))
Logging mergedArr to the console gives shows a new Array. The original data is still perfectly untouched. Again, shallow copies are what we are working with here.
Now the trick is to "Dedupe" the mergedArr. Lucky for us, ES6/7 makes this a piece of cake. Try:
const dedupedArr = [...new Set(mergedArr)]
Moving any further though requires some clarification on your end. You could make this much easier for yourself by providing a key (like generated a uuid) any time and items object is added to the Array. Then after that item moves to the datesToRemove Array, all you need to do is a .filter() off the instance ID key (uuid) to remove the right items safely, and without worrying about mutating any data along the way.
I'll leave you with these two finally alternatives,
const destructingOptionWithES6 = {
...items,
...datesToRemove
}
const ideallyAllYouShouldHaveToDoIsThisFunction = items.filter(val => !datesToRemove.includes(val))