22

I have an array of objects (objList) that each has "id" property.

I have an array of strings (idsToRemove), representing IDs of the objects to remove from objList.

I find some solution but I fear it's slow, especially with the large list of objects with lots of properties. Is there more efficient way to do this?

var idsToRemove = ["3", "1"];
var objList = [{
    id: "1",
    name: "aaa"
  },
  {
    id: "2",
    name: "bbb"
  },
  {
    id: "3",
    name: "ccc"
  }
];

for (var i = 0, len = idsToRemove.length; i < len; i++) {
  objList = objList.filter(o => o.id != idsToRemove[i]);
}

console.log(objList);
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Dalibor
  • 1,430
  • 18
  • 42

4 Answers4

37

Turn the idsToRemove into a Set so that you can use Set.prototype.has (an O(1) operation), and .filter the objList just once, so that the overall complexity is O(n) (and you only iterate over the possibly-huge objList once):

var idsToRemove = ["3", "1"];
var objList = [{
    id: "1",
    name: "aaa"
  },
  {
    id: "2",
    name: "bbb"
  },
  {
    id: "3",
    name: "ccc"
  }
];

const set = new Set(idsToRemove);
const filtered = objList.filter(({ id }) => !set.has(id));
console.log(filtered);

Note that Array.prototype.includes and Array.prototype.indexOf operations are O(N), not O(1), so if you use them instead of a Set, they may take significantly longer.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Not sure if the claim Set.has is O(1) is correct. Please provide an answer to this question if it is correct. https://stackoverflow.com/questions/55057200/is-set-has-method-o1-and-array-indexof-on – Charlie Mar 08 '19 at 05:55
  • Does it not depend on the size of the array? If the array is small, is it not better to through it than creating a new copy in memory then access it ? – Orelsanpls Mar 08 '19 at 07:58
  • 1
    You're right, if the `idsToRemove` has a very small number of elements, using a Set instead doesn't provide much of a benefit. – CertainPerformance Mar 08 '19 at 08:31
  • Can you explain me the difference between your syntax and this syntax: const filtered = objList.filter(o => !set.has(o.id)); – Dalibor Mar 09 '19 at 11:42
  • 1
    @Dalibor My version uses [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) in the argument list, whereas that version doesn't. If a function doesn't need the *whole* object, but only one (or some) of its properties, I like to extract that property immediately rather than having to hold on to the whole object - but both options work just fine. – CertainPerformance Mar 09 '19 at 11:48
4

You can use Array.includes which check if the given string exists in the given array and combine it with an Array.filter.

const idsToRemove = ['3', '1'];

const objList = [{
    id: '1',
    name: 'aaa',
  },
  {
    id: '2',
    name: 'bbb',
  },
  {
    id: '3',
    name: 'ccc',
  },
];

const filteredObjList = objList.filter(x => !idsToRemove.includes(x.id));

console.log(filteredObjList);
Orelsanpls
  • 22,456
  • 6
  • 42
  • 69
3

You don't need two nested iterators if you use a built-in lookup function

   objList = objList.filter(o => idsToRemove.indexOf(o.id) < 0);

Documentation:

Array.prototype.indexOf()

Array.prototype.includes()

Charlie
  • 22,886
  • 11
  • 59
  • 90
1

Simply use Array.filter()

const idsToRemove = ['3', '1'];

const objList = [{
    id: '1',
    name: 'aaa',
  },
  {
    id: '2',
    name: 'bbb',
  },
  {
    id: '3',
    name: 'ccc',
  }
];

const res = objList.filter(value => !idsToRemove.includes(value.id));

console.log("result",res);
dwirony
  • 5,487
  • 3
  • 21
  • 43
Khyati Sharma
  • 109
  • 1
  • 9