Get the array index of duplicates

Another less sophisticated approach:

Iterate over the whole array and keep track of the index of each element. For this we need a string -> positions map. An object is the usual data type to use for this. The keys are the elements of the array and the values are arrays of indexes/positions of each element in the array.

var map = {};

for (var i = 0; i < arr.length; i++) {
    var element = arr[i];  // arr[i] is the element in the array at position i

    // if we haven't seen the element yet, 
    // we have to create a new entry in the map
    if (!map[element]) {
        map[element] = [i];
    }
    else {
       // otherwise append to the existing array
        map[element].push(i);
    }
    // the whole if - else statement can be shortend to
    // (map[element] || (map[element] = [])).push(i)
}

Now you can iterate over the map and remove all entries where the array value has a length of one. Those are elements that appear only once in an array:

for (var element in map) {
    if (map[element].length === 1) {
        delete map[element];
    }
}

Now map contains a string -> positions mapping of all duplicate elements of the array. For example, if you array is ["abc","def","abc","xyz","def","abc"], then map is an object of the form

var map = {
    'abc': [0,2,5],
    'def': [1,4]
};

and you can process it further in any way you like.


Further reading:

  • Eloquent JavaScript - Data structures: Objects and Arrays
  • MDN - Working with objects
  • MDN - Predefined core objects, Array object

Update 01/2022: It's not 2013 anymore, and many things have changed. I neither recommend modifying the prototype, nor is the approach in this answer the "best" as it requires several iterations over the array.

Here's an updated version of the original answer, retaining its spirit, as well as the original answer below.

function getDuplicates<T>(input: T[]): Map<T, number[]> {
    return input.reduce((output, element, idx) => {
        const recordedDuplicates = output.get(element);
        if (recordedDuplicates) {
            output.set(element, [...recordedDuplicates, idx]);
        } else if (input.lastIndexOf(element) !== idx) {
            output.set(element, [idx]);
        }

        return output;
    }, new Map<T, number[]>());
}

Yet another approach:

Array.prototype.getDuplicates = function () {
    var duplicates = {};
    for (var i = 0; i < this.length; i++) {
        if(duplicates.hasOwnProperty(this[i])) {
            duplicates[this[i]].push(i);
        } else if (this.lastIndexOf(this[i]) !== i) {
            duplicates[this[i]] = [i];
        }
    }

    return duplicates;
};

It returns an object where the keys are the duplicate entries and the values are an array with their indices, i.e.

["abc","def","abc"].getDuplicates() -> { "abc": [0, 2] }