65

Here is my code for javascript for this simple task:

  1. Remove the element if it exists in an array.
  2. Add the element if it is not in an array.

    if(_.contains(this.types,type_id)){
        var index = this.types.indexOf(type_id);
        this.types.splice(index,1);
    }
    else{
        this.types.push(type_id);
    }
    

Is there a more efficient way to do this?

Damjan Pavlica
  • 31,277
  • 10
  • 71
  • 76
wwli
  • 2,623
  • 6
  • 30
  • 45

8 Answers8

66

You could do it without a 3rd party library, this would be more efficient, like this. (this only removes the first instance of a value if found, not multiple)

Javascript

var a = [0, 1, 2, 3, 4, 6, 7, 8, 9],
    b = 5,
    c = 6;

function addOrRemove(array, value) {
    var index = array.indexOf(value);

    if (index === -1) {
        array.push(value);
    } else {
        array.splice(index, 1);
    }
}

console.log(a);

addOrRemove(a, b);
console.log(a);

addOrRemove(a, c);
console.log(a);

Output

[0, 1, 2, 3, 4, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 6, 7, 8, 9, 5]
[0, 1, 2, 3, 4, 7, 8, 9, 5] 

On jsfiddle

Xotic750
  • 22,914
  • 8
  • 57
  • 79
  • 1
    This answer is ill-advised. A third-party library is not inherently less efficient than your own code. In fact, it is generally *more* efficient with developer resources since you don't need to reinvent the wheel. On top of that, the code shown here has the same suboptimal asymptotic complexity. The accepted answer by 6502, which actually achieves optimal complexity, is much better. – Julian May 05 '21 at 12:09
47

For immutable state ( clone array ):

const addOrRemove = (arr, item) => arr.includes(item) ? arr.filter(i => i !== item) : [ ...arr, item ];

see also: Remove properties from objects (JavaScript)

YairTawil
  • 3,961
  • 1
  • 22
  • 20
45

Using lodash "xor" function

_.xor([2, 1], [2, 3]);
// => [1, 3]

If you don't have an array as 2nd parameter you can simpy wrap the variable into an array

var variableToInsertOrRemove = 2;
_.xor([2, 1], [variableToInsertOrRemove]);
// => [1]
_.xor([1, 3], [variableToInsertOrRemove]);
// => [1, 2, 3]

Here's the doc: https://lodash.com/docs/4.16.4#xor

Kalnode
  • 9,386
  • 3
  • 34
  • 62
David Ginanni
  • 1,617
  • 15
  • 9
21

If you care about efficiency then may be using an array to implement a set is a bad idea. For example using an object you could do:

function toggle(S, x) {
    S[x] = 1 - (S[x]|0);
}

then after many add/remove operations you can keep only keys where the value is 1

This way every addition/removal is O(1) and you need only one O(n) operation to get the final result.

If keys are all "small" numbers may be a bitmask is even worth the effort (not tested)

function toggle(S, x) {
    var i = x >> 4;
    S[i] = (S[i]|0) ^ (1<<(x&15));
}
6502
  • 112,025
  • 15
  • 165
  • 265
5

Look at this answer of similar question.

Lodash issue

Lodash gist

Code:

function toggle(collection, item) {
  var idx = collection.indexOf(item);
  if (idx !== -1) {
    collection.splice(idx, 1);
  } else {
    collection.push(item);
  }
}
WebBrother
  • 1,447
  • 20
  • 31
  • 5
    This is just a copy of Xotic750's answer with renamed variables. – Hybrid web dev May 03 '21 at 23:14
  • This is not a Xotic750 answer! Please read my answer more attentively! The idea was taken from @nnnnnn answer but not Xotic750! And I mentioned it in my answer in the 1st link (https://stackoverflow.com/a/18765477/2179748) ! – WebBrother Dec 26 '22 at 11:13
2

If "types" can be a Set then

let toggle = type_id => this.types.delete(type_id) || this.types.add(type_id);
nclu
  • 1,057
  • 1
  • 8
  • 19
1

Using underscorejs

function toggle(a,b)
{
return _.indexOf(a,b)==-1?_.union(a,[b]):_.without(a,b);
}

Usage:

var a = [1,2,3];
var b = [4];
a = toggle(a,b); // [1,2,3,4]
a = toggle(a,b); // [1,2,3]
Flavien Volken
  • 19,196
  • 12
  • 100
  • 133
1

Extending Xotic750's answer this will always ensure that toggled elements occur only once in array. You this if your arrays are bit random like user inputs.

function toggleValueInArray(array, value) {
  var index = array.indexOf(value);

  if (index == -1) {
    array.push(value);
  } else {
    do {
      array.splice(index, 1);
      index = array.indexOf(value);
    } while (index != -1);
  }
}


var a = [0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9],
  b = 5,
  c = 10;

// toString just for good output
console.log(a.toString());

toggleValueInArray(a, b);
console.log(a.toString());

toggleValueInArray(a, c);
console.log(a.toString());