I'm looking for an efficient JavaScript utility method that in O(n) will remove a set of items from an array in place. You can assume equality with the === operator will work correctly.
Here is an example signature (written in TypeScript for type clarity)
function deleteItemsFromArray<T>(array: T[], itemsToDelete: T[]) { ... }
My thought is to do this in two passes. The first pass gets the indexes that need to be removed. The second pass then pacts the array by copying backwards from the current index being removed through the next index being removed.
Does anyone have code like this already handy or a more efficient way to do this?
P.S. Please don't point out the filter
function as that creates a copy of the array, it does not work in place.
I'm looking for an efficient JavaScript utility method that in O(n) will remove a set of items from an array in place. You can assume equality with the === operator will work correctly.
Here is an example signature (written in TypeScript for type clarity)
function deleteItemsFromArray<T>(array: T[], itemsToDelete: T[]) { ... }
My thought is to do this in two passes. The first pass gets the indexes that need to be removed. The second pass then pacts the array by copying backwards from the current index being removed through the next index being removed.
Does anyone have code like this already handy or a more efficient way to do this?
P.S. Please don't point out the filter
function as that creates a copy of the array, it does not work in place.
- Filter, then delete the contents of the original and expand the copy into it? I dunno if that would be more efficient though. – John Montgomery Commented Jun 5, 2020 at 21:46
- If I understand you correct you have O(2*n) that is equal to O(n), no? – vadimk7 Commented Jun 5, 2020 at 21:48
- I don't want to copy the data at all. This needs to be pletely in place. Memory is an issue. – MgSam Commented Jun 5, 2020 at 21:48
- 1 @v.kostenko Yes, O(2n) == O(n). I haven't coded the algorithm yet. Wanted to see if the wheel exists somewhere before I go re-invent it. – MgSam Commented Jun 5, 2020 at 21:49
-
Why not pass a set for
itemsToDelete
? Then it's trivial. Otherwise you just have to transform it to a set insidenew Set(itemsToDelete)
– VLAZ Commented Jun 5, 2020 at 21:49
4 Answers
Reset to default 10Iterate over the array, copying elements that aren't in itemsToDelete
to the next destination index in the array. When you delete an element, you don't increment this index.
Finally, reset the length of the array at the end to get rid of the remaining elements at the end.
function deleteItemsFromArray(array, itemsToDelete) {
let destIndex = 0;
array.forEach(e => {
if (!itemsToDelete.includes(e)) {
array[destIndex++] = e;
}
});
array.length = destIndex;
}
const array = [1, 2, 3, 4, 5, 6, 7];
deleteItemsFromArray(array, [3, 5]);
console.log(array);
function deleteItems(array,items){
let itemsToDelete=0;
let indexToSwap = array.length-1;
for(let i = array.length-1,currItem ; i>=0 ; i--){
if(items.includes(array[i]){
[array[i] , array[indexToSwap]] = [array[indexToSwap] , array[i]];
--indexToSwap;
++itemsToDelete;
}
}
array.splice(array.length-itemsToDelete,itemsToDelete);
}
This should work, I haven't tested it.
The idea is to swap the elements to delete to the end of the array. You can remove them at the end how I do in my code or you could use too the pop() function every time.
It's very, very simple - transform itemsToDelete
to a Set. This ensures O(1)
lookups. Then walk through the array backwards and remove items with Array#splice
. In total, that gives you a linear O(n+m)
time plexity:
function deleteItemsFromArray<T>(array: T[], itemsToDelete: T[]) {
const setToDelete = new Set(itemsToDelete);
for (let i = array.length - 1; i >= 0; i--) {
const item = array[i];
const shouldBeDeleted = setToDelete.has(item);
if (shouldBeDeleted) {
array.splice(i, 1);
}
}
}
You can save the conversion step if you just make the function accept a set to begin with and change the signature to:
function deleteItemsFromArray<T>(array: T[], itemsToDelete: Set<T>)
function deleteItemsFromArray(array, itemsToDelete) {
for (let i = array.length - 1; i >= 0; i--) {
const item = array[i];
const shouldBeDeleted = itemsToDelete.has(item);
if (shouldBeDeleted) {
array.splice(i, 1);
}
}
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
deleteItemsFromArray(
arr, new Set([1, 3, 5, 7, 8, 9])
)
console.log(arr);
Using copyWithin
method will be helpful here.
method 1 Traverse from last index to first index, when ever we find the item to remove just copy/move elements to left. Though we will be doing times the copy/move, But still have some unnecessary moving elements.
method 2 Traverse from first index to last index, when ever we find the item to remove, identify the group of elements to copy/move. This will avoid the unnecessary moving elements as in method 1.
function deleteItemsFromArray(array, delete_list) {
for (let i = array.length - 1; i > -1; i--) {
if (delete_list.includes(array[i])) {
array.copyWithin(i, i + 1).pop();
}
}
}
// Alternate way, with minimal copy/move group of elements.
function deleteItemsFromArrayAlternate(array, delete_list) {
let index = -1;
let count = 0;
for (let i = 0; i <= array.length; i++) {
if (delete_list.includes(array[i]) || !(i in array)) {
if (index > -1) {
array.copyWithin(index - count + 1, index + 1, i);
}
count += 1;
index = i;
}
}
array.length = array.length - delete_list.length;
}
const array = [9, 12, 3, 4, 5, 6, 7];
deleteItemsFromArray(array, [9, 6, 7]);
console.log(array);
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745404624a4626258.html
评论列表(0条)