11

Following an old question, I still have a problem:

a = ["apple", "banana", "orange", "apple"];

a.indexOf("apple") = 0

What is the easiest way to find BOTH indexes of "apple" element in array? I want to delete them both at once - is it possible?

Community
  • 1
  • 1
Prosto Trader
  • 3,471
  • 3
  • 31
  • 52
  • 2
    Great question which, while simple at the onset, has produced a variety of answers and all different approaches. I learned something from these answers, and thank you for asking the question. – Dmitriy Khaykin Feb 09 '14 at 18:11
  • Question title is misleading. Unfortunately, it comes up in Google search. – MKaama Oct 22 '18 at 13:03

10 Answers10

15

That's the task for filter method:

var noApples = a.filter(function(el) { return el != "apple"; })
c-smile
  • 26,734
  • 7
  • 59
  • 86
  • 5
    +1 This is a great answer, and should be preferred where possible. However, it's worth noting that it will *not* modify the original Array instance, and instead produces a new array instance containing the filtered results. In cases where the original array reference *must* be modified (these cases are rare), `splice` may be used instead. – zzzzBov Feb 09 '14 at 18:24
  • +1 It's also worth noting that the `filter` method is sadly not supported in IE8 and below. – Boaz Feb 13 '14 at 13:59
9

What is the easiest way to find BOTH indexes of "apple" element in array?

You asked that, but also asked about deleting. I'll tackle indexes first, then deletion.

Indexes:

There's no shortcut, you have to loop through it. You can use a simple for loop:

var indexes = [];
var index;

for (index = 0; index < a.length; ++index) {
    if (a[n] === "apple") {
        indexes.push(index);
    }
});

Or two ES5 options: forEach:

var indexes = [];
a.forEach(function(entry, index) {
    if (entry === "apple") {
        indexes.push(index);
    }
});

Or reduce:

var indexes = a.reduce(function(acc, entry, index) {
    if (entry === "apple") {
        acc.push(index);
    }
    return acc;
}, []);

...although frankly that does't really buy you anything over forEach.

Deletion:

From the end of your question:

I want to delete them both at once - is it possible?

Sort of. In ES5, there's a filter function you can use, but it creates a new array.

var newa = a.filter(function(entry) {
    return entry !== "apple";
});

That basically does this (in general terms):

var newa = [];
var index;

for (index = 0; index < a.length; ++index) {
    if (a[n] !== "apple") {
        newa.push(index);
    }
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • `forEach` is not an option if you need to support IE8. BTW, i'm not the downvoter. – gearsdigital Feb 09 '14 at 18:04
  • 1
    @gearsdigital: 1. Sure it is, it's easily shimmable. 2. I did point out it's ES5-only. 3. I decided to give an example of `for` as well. – T.J. Crowder Feb 09 '14 at 18:05
  • Using `Array.reduce()` for that purpose is wrong. Why do you need it there? – c-smile Feb 09 '14 at 18:08
  • 1
    @c-smile: It's not *wrong*, in that it does the job. (http://jsbin.com/purex/1/edit) One of the things about `reduce` is that it can be used for all sorts of things. – T.J. Crowder Feb 09 '14 at 18:10
  • My comment was not meant to be critical! Natively the IE8 does not support forEach, that is what wanted to point out. And as you permanently edit your answer it's hard to match a state... ;) – gearsdigital Feb 09 '14 at 18:11
  • 3
    @gearsdigital: Not always, only when I think of something to add. I try to make sure the *very first* answer is useful and sufficiently-complete (in case I get called away). (I failed at that this time, though; I missed the last sentence of the question until I went back to do a live example!) :-) – T.J. Crowder Feb 09 '14 at 18:13
  • @T.J.Crowder Yes, you technically can use reduce() to simulate filter() behavior but a) filter() is there and b) it is faster as it may use optimizations like preallocation of the output array, etc. – c-smile Feb 09 '14 at 18:17
  • @T.J.Crowder To enhance and improve answers, is one of most useful functionality here at SO. Glad you used it! – gearsdigital Feb 09 '14 at 18:17
3

Array.indexOf takes a second, optional argument: the index to start from. You can use this inside a loop to specify to start from the last one.

var indices = [],
    index = 0;

while (true) {
    index = a.indexOf("apple", index);
    if (index < 0) {
        break;
    }
    indices.push(index);
}

Once indexOf returns -1, which signals "no element found", the loop will break. The indices array will then hold the correct indices.

There is an example on the Mozilla page on indexOf which has some equivalent code. I'm not so much of a fan because of the increased duplication, but it is shorter, which is nice.

Samir Talwar
  • 14,220
  • 3
  • 41
  • 65
2

A for loop will do the trick. Or use forEach as T.J. Crowder suggests in his elegant answer.

I combined both an example of how to get appleIndexes and also how to "delete" them from the original array by virtue of creating a new array with all but apples in it. This is using oldSchool JavaScript :)

a = ["apple", "banana", "orange", "apple"];

appleIndexes = [];
arrayOfNotApples = [];

for (var i = 0; i < a.length; i++)
{
    if (a[i] == "apple")
    {
        appleIndexes.push(i);
    } else {
        arrayOfNotApples.push(a[i]);
    }
}
Dmitriy Khaykin
  • 5,238
  • 1
  • 20
  • 32
2

If you need to remove elements from an array instance without generating a new array, Array.prototype.splice is a good choice:

var a,
    i;
a = ["apple", "banana", "orange", "apple"];
for (i = a.indexOf('apple'); i > -1; i = a.indexOf('apple')) {
    a.splice(i, 1);
}

If you can use a new array instance, then Array.prototype.filter is a better choice:

var a,
    b;
a = ["apple", "banana", "orange", "apple"];
b = a.filter(function (item, index, array) {
    return item !== 'apple';
});
zzzzBov
  • 174,988
  • 54
  • 320
  • 367
1

Use the start parameter in array.indexOf(element, start), as described in http://www.w3schools.com/jsref/jsref_indexof_array.asp.

Example:

var a = [1, 3, 4, 1];
var searchElement = 1;
var foundIndices = [];
var startIndex = 0;

while ((index = a.indexOf(searchElement, startIndex)) != -1) {
       foundIndices.push(index);
       startIndex = index + 1;
}
console.log(foundIndices); // Outputs [0, 3];
Hidde
  • 11,493
  • 8
  • 43
  • 68
1

A good old while loop :

var i = a.length;
while (i--) {
    if (a[i] === 'apple') {
        a.splice(i, 1); 
    }
}

Inside a function :

function removeAll(value, array) {
    var i = array.length;
    while (i--) {
        if (array[i] === value) {
            array.splice(i, 1); 
        }
    }
    return array;
}

Usage :

removeAll('apple', a);
1

A couple of recursive solutions.

Javascript

function indexesOf(array, searchElement, fromIndex) {
    var result = [],
        index = array.indexOf(searchElement, fromIndex >>> 0);

    if (index === -1) {
        return result;
    }

    return result.concat(index, indexesOf(array, searchElement, index + 1));
}

function removeFrom(array, searchElement, fromIndex) {
    var index = array.indexOf(searchElement, fromIndex >>> 0);

    if (index !== -1) {
        array.splice(index, 1);
        removeFrom(array, searchElement, index);
    }

    return array;
}

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

console.log(indexesOf(a, 0));
console.log(removeFrom(a, 0));

Output

[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

On jsFiddle

Xotic750
  • 22,914
  • 8
  • 57
  • 79
1

The fastest, most compatible, route would be to walk the array backwards in a for loop.

for (var a = array.length;a--;)
     if (array[a] == 'apple') array.splice(a,1);
PatAtCP
  • 577
  • 3
  • 10
0

if you want to remove all occurrences, you could also use Array.splice recursively

function remove(list, item) {
    if(list.indexOf(item)<0)
        return list;

    list.splice(list.indexOf(item),1);
    return list;
}