7

I have an interface with some fields like that:

interface Item {
    id: number;
    name: string;
}

And I want to implement a method removeItemsWithName(items: Item[], name: string) which removes all items from the given array, which have the given name:

const myItems: Item[] = [
    { id: 1, name: 'foo' },
    { id: 2, name: 'foo' },
    { id: 3, name: 'bar' }
];

removeItemsWithName(myItems, 'foo');
console.log(myItems);

The result should be:

[{id: 3, name: 'bar'}]

Is there a method array.removeIf() (like Collection.removeIf() in Java 8) available in typescript/javascript, so that my method could look like something similar to this:

function removeItemsWithName(items: Item[], name: string): void {
    items.removeIf(i => i.name === name);
}

I already found the question Remove array element based on object property, but I need to modify the original array in place instead of creating a new one (like array.filter() does). I also want to remove all items matching the condition, not just the first one.

Samuel Philipp
  • 10,631
  • 12
  • 36
  • 56
  • Possible duplicate of [Remove array element based on object property](https://stackoverflow.com/questions/15287865/remove-array-element-based-on-object-property) – Heretic Monkey May 03 '19 at 15:37
  • @HereticMonkey I already noticed that question before, but forgot to mention this in my question. I edited my post now to clarify it. – Samuel Philipp May 03 '19 at 16:05
  • I notice array question always gets more answers than others :P – hackape May 03 '19 at 16:12
  • There are more answers on that question than the accepted one. [This answer](https://stackoverflow.com/a/15287919/215552) for instance, uses `splice` in a loop. – Heretic Monkey May 03 '19 at 16:22
  • @HereticMonkey Thanks for referencing this, I must have missed that answer. – Samuel Philipp May 03 '19 at 16:45

4 Answers4

15

The standard solution would be filter, but this returns a new array:

function removeItemsWithName(items: Item[], name: string): Item[] {
  return items.filter(i => i.name !== name);
}

However, if you really need to modify the array in place, you'd have to use something like this:

function removeItemsWithName(items: Item[], name: string): void {
  let j = 0;
  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    if (item.name !== name) {
        items[j] = item;
        j++;
    }
  }

  items.length = j;
}

Or with splice:

function removeItemsWithName(items: Item[], name: string): void {
  for (let i = 0; i < items.length; i++) {
    if (items[i].name === name) {
      items.splice(i--, 1);
    }
  }
}

Or you can use a library like lodash which has a convenient remove method:

import _remove from 'lodash/remove';

function removeItemsWithName(items: Item[], name: string): void {
  _remove(items, x => x.name === name);
}
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
3

Too many ways to do this, I suggest use filter function.

Try this:

function removeItemsWithName(items: Item[], name: string): void {
  items = items.filter(i => i.name !== name);
}

I think it will be better if removeItemsWithName function returns a new array and does not change params value.

function removeItemsWithName(items: Item[], name: string): Item[] {
  return items.filter(i => i.name !== name);
}

const myItems: Item[] = [
  { id: 1, name: 'foo' },
  { id: 2, name: 'foo' },
  { id: 3, name: 'bar' }
];

const filtered = removeItemsWithName(myItems, 'foo');
console.log(filtered);

Update

You can create your owner Collection with removeIf method.

interface Item {
  id: number;
  name: string;
}

class MyCollection<T> {
  private items: Array<T>;

  constructor(init?: T[]) {
    this.items = init || [];
  }

  get(): T[] {
    return this.items;
  }

  removeIf(conditionFunction: (i: T) => boolean) {
    this.items = this.items.filter(i => !conditionFunction(i));
  }
}

const myItems: MyCollection<Item> = new MyCollection<Item>([
  { id: 1, name: "foo" },
  { id: 2, name: "foo" },
  { id: 3, name: "bar" }
]);

function removeItemsWithName<T extends Item>(
  items: MyCollection<T>,
  name: string
): void {
  items.removeIf(i => i.name === name);
}

removeItemsWithName(myItems, "foo");
console.log(myItems.get());
hoangdv
  • 15,138
  • 4
  • 27
  • 48
2

You could use Array.prototype.filter (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter):

const myItems = [{
    id: 1,
    name: 'foo'
  },
  {
    id: 2,
    name: 'foo'
  },
  {
    id: 3,
    name: 'bar'
  }
];

console.log(myItems.filter((item) => item.name !== 'foo'))

So your function would simply be:

const removeItemsWithName = (items: Item[], name: string): Item[] => items.filter((item: Item) => item.name !== name);
Clément P.
  • 39
  • 1
  • 4
1

Take one, leverage .filter(). Less code but O(2n).

function removeItemsWithName(items: Item[], name: string): void {
  var newArray = items.filter(i => i.name !== name);
  items.length = 0; // this empty the original array
  newArray.forEach(item => { items.push(item) });
}

Take two, use .splice(), the old school O(n) way.

function removeItemsWithName(items: Item[], name: string): void {
  var index = items.length - 1;
  var predicate = i => i.name === name;
  while(index >= 0) {
    if (predicate(items[index])) {
      items.splice(index, 1)
    }
    index--;
  }
}
hackape
  • 18,643
  • 2
  • 29
  • 57