tags:

views:

841

answers:

6

What's the simplest, library-free code for implementing array intersections in javascript? I want to write

intersection([1,2,3], [2,3,4,5])

and get

[2, 3]
+1  A: 

Use a combination of filter and indexOf.

EDIT: If you really want a code sample:

array1.filter(function(n) {
        if(array2.indexOf(n) == -1)
            return false;
        return true;
    });

Illustrates the concept, at least.

Anon.
In the worst case, there is no intersection. array2.indexof(n) will have to check every element of array2 for each element of array1. Complexity should be O(n*m). On larger arrays, this will become very slow.
atk
`filter` and `indexOf` are not supported in some browsers, notably IE.
Tim Down
Which you can solve by adding a library version on array's prototype.
Anon.
Yes, but it was worth mentioning.
Tim Down
Could you just do `return (array2.indexOf(n) != -1)` or am I missing something?
alex
Yes, you could. Unless we're both missing something. I think this version illustrates it more clearly, though.
Anon.
A: 
  1. Sort it
  2. check one by one from the index 0, create new array from that.

Something like this, Not tested well though.

function intersection(x,y){
 x.sort();y.sort();
 var i=j=0;ret=[];
 while(i<x.length && j<y.length){
  if(x[i]<y[j])i++;
  else if(y[j]<x[i])j++;
  else {
   ret.push(x[i]);
   i++,j++;
  }
 }
 return ret;
}

alert(intersection([1,2,3], [2,3,4,5]));

PS:The algorithm only intended for Numbers and Normal Strings, intersection of arbitary object arrays may not work.

S.Mark
Sorting will not necessarily help for arrays of arbitrary objects
Tim Down
If the array is not sorted, need to loop around 1,000,000 times when you intersect 1000 length array x 1000 length array
S.Mark
I think you missed my point, which is that arbitrary objects in JavaScript have no natural sort order, meaning that sorting an array of arbitrary objects will not result in equal objects being grouped. It's no good having an efficient algorithm that doesn't work.
Tim Down
Ah sorry, I missed "arbitrary objects", yes, You're right. those object cannot sort it, and the algorithm may not work on those.
S.Mark
+1  A: 
atk
There's numerous errors in `intersect_safe`: `length` is a property in Arrays, not a method. There's an undelared variable `i` in `result.push(a[i]);`. Finally, this simply doesn't work in the general case: two objects where neither is greater than the other according to the `>` operator are not necessarily equal. `intersect_safe( [ {} ], [ {} ] )`, for example, will give (once the previously mentioned errors are fixed) an array with one element, which is clearly wrong.
Tim Down
@Tim Down: Corrected the syntax errors you pointed out. Whether it's correct or incorrect to consider anything neither gerater nor less to be equal depends upon the requirements. I don't notice anything in the original question saying that the contents are expected to contain arrays. Now, you'd be right to say that unexpected input should be handled, but if the spec already happened to dictate that input must be arrays of numbers (as I assumed) then the code would be fine.
atk
@atk: I take your point, seeing as the example in the question uses arrays that only contain numbers.
Tim Down
A: 

How about just using associative arrays?

function intersect(a, b) {
    var d1 = {};
    var d2 = {};
    var results = [];
    for (var i = 0; i < a.length; i++) {
        d1[a[i]] = true;
    }
    for (var j = 0; j < b.length; j++) {
        d2[b[j]] = true;
    }
    for (var k in d1) {
        if (d2[k]) 
            results.push(k);
    }
    return results;
}
Steven Huwig
This only stands a chance if your arrays only contain strings or numbers, and if none of the script in your page has messed with `Object.prototype`.
Tim Down
The OP's example was using numbers, and if a script has messed with Object.prototype then the script should be rewritten or removed.
Steven Huwig
A: 

"filter" and "indexOf" aren't supported on Array in IE. How about this:

var array1 = [1, 2, 3];
var array2 = [2, 3, 4, 5];

var intersection = [];
for (i in array1) {
    for (j in array2) {
        if (array1[i] == array2[j]) intersection.push(array1[i]);
    }
}
darkporter
don't ever use `for .. in` on arrays. ever. geez.
nickf
Well he said "no library" so it should be safe from iterable Array prototype extensions. But ya, that's good general advice.
darkporter
+1  A: 

For arrays containing only strings or numbers you can do something with sorting, as per some of the other answers. For the general case of arrays of arbitrary objects I don't think you can avoid doing it the long way. The following will give you the intersection of any number of arrays provided as parameters to arrayIntersection:

var arrayContains = Array.prototype.indexOf ?
    function(arr, val) {
        return arr.indexOf(val) > -1;
    } :
    function(arr, val) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === val) {
                return true;
            }
        }
        return false;
    };

function arrayIntersection() {
    var val, arrayCount, firstArray, i, j, intersection = [], missing;
    var arrays = Array.prototype.slice.call(arguments); // Convert arguments into a real array

    // Search for common values
    firstArr = arrays.pop();
    if (firstArr) {
        j = firstArr.length;
        arrayCount = arrays.length;
        while (j--) {
            val = firstArr[j];
            missing = false;

            // Check val is present in each remaining array 
            i = arrayCount;
            while (!missing && i--) {
                if ( !arrayContains(arrays[i], val) ) {
                    missing = true;
                }
            }
            if (!missing) {
                intersection.push(val);
            }
        }
    }
    return intersection;
}

arrayIntersection( [1, 2, 3, "a"], [1, "a", 2], ["a", 1] ); // Gives [1, "a"];
Tim Down
This only works in the case where object identity is the only form of equality.
Steven Huwig
Well yes, but I think that's what would seem natural to most people. It's also trivial to plug in an alternative function to perform a different equality test.
Tim Down