简体   繁体   中英

Efficient way to delete from an array of objects in JavaScript

frontendtasks = [
                 {"id": 1, "name": "User Deletion", "script": "UserDeletion"},
                 {"id": 2, "name": "User Creation", "script_name": "UserCreation"}
                ]

backendtasks = [
                {"id": 1, "name": "User Deletion", "script": "UserDeletion_V2"}
               ]

I'm trying to delete the entry with id = 1 in frontendtask and push the entry from backendtask with this code.

if (backendtasks != 0) {
    for (updated_task in backendtasks ) {
        for (oldtask in frontendtasks) {
            if (frontendtasks[oldtask].id == backendtasks[updated_task].id) {
                frontendtasks[oldtask] = backendtasks[updated_task]
                delete backendtasks[updated_task];
                break;
            }
        }
    }
    for (new_task in backendtasks) {
        frontendtasks.unshift(backendtasks[new_task])
    }
}

This is really slow and CPU hits 100% in browser with 700 items. Is there any efficient way to implement this?

Don't loop through both arrays, instead use an object to map backend ids to values:

const mappings = {};
for (const task of backendtasks) {
    mappings[task.id] = task;
}
for (let i = 0; i < frontendtasks.length; i ++) {
    const curid = frontendtasks[i].id;
    if (curid in mappings) {
        frontendtasks[i] = mappings[curid];
        delete mappings[curid];
    }
}
// push is faster than unshift
for (const key in mappings) {
    frontendtasks.push(mappings[key]);
}

Approach: Since you have 2 arrays, I would suggest first normalizing backend array to an object, and then iterate on frontend array and lookup in normalized object as lookup in object is O(1) as compared to O(n) in array.

 function getFrontendTasks(){ const frontendtasks = [ {"id": 1, "name": "User Deletion", "script": "UserDeletion"}, {"id": 2, "name": "User Creation", "script_name": "UserCreation"} ] const backendtasks = [ {"id": 1, "name": "User Deletion", "script": "UserDeletion_V2"} ] const normalizedBackendTasks = backendtasks.reduce((acc, val) => ({...acc, [val.id]: val}), {}); const newFrontendTasks = frontendtasks.map((task) => normalizedBackendTasks[task.id] || task); return newFrontendTasks } console.log(getFrontendTasks())

Creating a mapping table reduces the time complexity from O(n^2) to O(n) , by removing the nested for loops, which is very expensive.

Try the following code:

const map = {};
backendtasks.forEach(bt => (map[bt.id] = bt));

frontendtasks.forEach((ft, idx) => {
  if (map[ft.id]) {
    frontendtasks[idx] = map[ft.id];
    delete map[ft.id];
  }
});

frontendtasks = frontendtasks.concat(Object.values(map));

Somehow I didn't see the map() function in any solution that creates a new array as shown below.

This will return the new array with the new value. As you can see, it takes an array, an id (this could be anything and any type tho), and a callback.

Searcing for the id in the array and runs the callback when found. Efficient way for what you want to do.

In the callback, I used find() on the backendtasks simply because I need to find the item which has the same id (id: 1).

When found, it returns the item from backendtasks then completes the function by returning that value in the map() function.

So, this should be O(n) , considering that the callback only runs once and it's a more elegant solution for multiple uses in my opinion.

const frontendtasks: any[] = [];
const backendtasks: any[] = [];

const fn = (arr: any[], id: number, callback: (removed: any) => any) => {
  return arr.map((ft) => {
    if (ft.id !== id) return ft;
    else return callback(ft);
  });
};

fn(frontendtasks, 1, (rm) => backendtasks.find((id) => rm.id === id));
frontendtasks = [
                 {"id": 1, "name": "User Deletion", "script": "UserDeletion"},
                 {"id": 2, "name": "User Creation", "script_name": "UserCreation"}
                ]

backendtasks = [
                {"id": 1, "name": "User Deletion", "script": "UserDeletion_V2"}
               ]

I'm trying to delete the entry with id = 1 in frontendtask and push the entry from backendtask with this code.

if (backendtasks != 0) {
    for (updated_task in backendtasks ) {
        for (oldtask in frontendtasks) {
            if (frontendtasks[oldtask].id == backendtasks[updated_task].id) {
                frontendtasks[oldtask] = backendtasks[updated_task]
                delete backendtasks[updated_task];
                break;
            }
        }
    }
    for (new_task in backendtasks) {
        frontendtasks.unshift(backendtasks[new_task])
    }
}

This is really slow and CPU hits 100% in browser with 700 items. Is there any efficient way to implement this?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM