8

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.

5
  • Possible duplicate of Remove array element based on object property Commented May 3, 2019 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. Commented May 3, 2019 at 16:05
  • I notice array question always gets more answers than others :P Commented May 3, 2019 at 16:12
  • There are more answers on that question than the accepted one. This answer for instance, uses splice in a loop. Commented May 3, 2019 at 16:22
  • @HereticMonkey Thanks for referencing this, I must have missed that answer. Commented May 3, 2019 at 16:45

4 Answers 4

16

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);
}
Sign up to request clarification or add additional context in comments.

Comments

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());

4 Comments

What if mutating the original array is a requirement? Like stated in OP by removeItemsWithName(...): void
@hackape overwrite original array value, items = removeItemsWithName(myItems, 'foo')
@hoangdv thanks for your answer, but overwriting the original array is not an option. I need to modify the given array.
@hoangdv you miss the point, keeping the original array reference is a requirement.
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);

1 Comment

Thanks for you answer. Unfortunately I need to modify the given array and can not create a new one.
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--;
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.