4

I would like to know how to compare two or more -- potentially unlimited -- arrays for common values and push these values into a new array efficiently. Below I have a function that will accept unlimited arguments, but I am uncertain if this is a good place to begin. PHP appears to have a method that can do what I want called array_intersect. Does javascript offer something similar?

Note: I have found examples of how this can be done with two or so arrays, but I have not found examples of how such approaches might be applied to an unspecified number of arrays as of yet. Therefore I do not see this as a duplicate question.

To further clarify, the arrays might be filled with anything. Letters, numbers, symbols, words, you name it, it might be there.

var sampleOne = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var sampleTwo = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];

function FindDirectRelation() {
    for(var i = 0; i < arguments.length; ++i) {
        console.log(arguments[i]);
        

    };
};

var directRelation = FindDirectRelation(sampleOne, sampleTwo);

I am still a coding novice, so please ensure that everything is explained in a way that is simple enough for me to understand.

Laurence
  • 43
  • 3
  • _"potentially unlimited -- arrays "_ ? What is expected result of comparison ? – guest271314 Dec 21 '15 at 06:02
  • 1
    I have to say, if all new member questions could be this well-written our job would be much simpler :) – Domino Dec 21 '15 at 06:04
  • Interestingly enough, a search should have revealed the link posted by @DanDavis – mplungjan Dec 21 '15 at 06:07
  • @guest271314 I am trying to design a basic learning algorithm, and I need this function for it to find direct relations between two or more data sets. I might give it ten data sets or just two. – Laurence Dec 21 '15 at 06:10
  • @dandavis I really do not know how to incorporate it. The example that you linked is a little bit over my head, but I will continue to try to understand it. – Laurence Dec 21 '15 at 06:12
  • @Laurence What is expected result of `FindDirectRelation(sampleOne, sampleTwo);` ? – guest271314 Dec 21 '15 at 06:13
  • @guest271314 To find the common values shared by two or more arrays and push them into a new array. These common values must be shared by all of the arrays. – Laurence Dec 21 '15 at 06:19
  • You might want to consult the Underscore source code for `_.intersection`, which does exactly this. –  Dec 21 '15 at 19:55

3 Answers3

5

using an existing intersect that works with 2 arrays, we can chain together a common sub-set using the built-in reduce() method on an array of arrays that need intersected:

function intersect(a, b) {
  var aa = {};
  a.forEach(function(v) { aa[v]=1; });
  return b.filter(function(v) { return v in aa; });
}

var r1=[1,2,3], 
r2=[1,3,4,5], 
r3=[5,1,3];

alert([r1, r2, r3].reduce(intersect)) // shows: 1,3

if you define "intersect" as just being in more than one array (not every), then it's more complex...

dandavis
  • 16,370
  • 5
  • 40
  • 36
  • Yes, I mean an intersect found in every array. Yet that has got me thinking of the benefits of identifying less frequent intersections. Give me a chance to grasp this... – Laurence Dec 21 '15 at 06:24
  • keep in mind that you can pass more than 3 arrays in this manner. for perf, you can put the most different arrays (often smallest and longest) up front, if that's possible (the result is the same no matter order). Also, that intersect function is for strings/numbers, but any working intersect(a,b) function can be swapped in as needed. – dandavis Dec 21 '15 at 06:27
  • I am not entirely sure yet how this works, but it does! It works with letters / strings too. Now I just need to wrap my head around this... Thanks a tonne! – Laurence Dec 21 '15 at 06:42
  • the way that intersect function works is to use an object to store the values as keys. this lets us quickly ask if an object already has a value much faster than indexOf()-based solutions (a property lookup vs a sub-iteration). the reduce part is more complicated, and you should look that up instead of having me try to explain it, but it basically runs left to right treating the result of the last run as one of the 2 args of the current run, the element to the right being the other.... – dandavis Dec 21 '15 at 06:48
  • This should not be the accepted answer, because `intersect([1],["1"])` yields `["1"]`. It also does not work for elements which are not valid keys, such as objects or functions, if that is an issue. –  Dec 21 '15 at 08:26
  • @torazaburo: i tried to explain the type limits, sorry if it was unclear. That said, i still think that the `reduce` usage is the key here; any `intersect(a,b)` function can be substituted in my answer; for example, one using indexOf() to differentiate between `"5"`, `/5/`, `[5]`, and `5`. Using an object key to reconcile is very fast (n times indexOf), but with the type limit you mention. aside: i try to not complain about other answers if i post my own answer, it looks like campaigning... – dandavis Dec 21 '15 at 19:39
  • I agree your overall solution is highly elegant. However, your optimization may have been going a bit too far, I'd fall back to `indexOf`. –  Dec 21 '15 at 19:50
0

Check to make sure the elements in the first array are also in the remaining arrays:

function multi_intersect(a) {
  var other_arrays = Array.prototype.slice.call(arguments, 1);

  return a . filter(function(elt) {
    return other_arrays.every(function(an) {
      return an.indexOf(elt) !== -1;
    });
  });
}
-1

Try using Array.prototype.filter() , Array.prototype.indexOf()

var res = sampleOne.filter(function(val) {return sampleTwo.indexOf(val) !== -1})

var sampleOne = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var sampleTwo = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
var arr = ["a", "b", "c"];
var arr1 = ["c", "d", "e"];
var arr2 = [2, 7];

function samples() {
  var args = Array.prototype.slice.call(arguments);
  var res = [];
  for (var i = 0, curr, next; i < args.length; i++) {
    if (args[i + 1]) {
      // set `curr` to array `i`
      curr = args[i];
      // set `next` to array `i + 1` if it exists
      next = args[i + 1]
    } else {
      // if at last index, set `curr` to `args` : input arrays 
      // flattened to single array , with element at `i` removed
      curr = [].concat.apply([], args.slice(0, args.length - 1));
      console.log(curr)
      // set next to current index
      next = args[i];
    };
    next = next.filter(function(val) {
      return curr.indexOf(val) !== -1 
             // filter duplicate entries at `res`
             && res.indexOf(val) === -1
    });
    res = res.concat.apply(res, next);
  };
  return res
}
var sample = samples(sampleOne, sampleTwo, arr, arr1, arr2);
console.log(sample); // [5, 6, 7, 8, 9, 10, 11, 12, "c", 2]
guest271314
  • 1
  • 15
  • 104
  • 177
  • How does this deal with multiple input arrays? –  Dec 21 '15 at 06:45
  • I think, but am not sure, that he wants values in **all** the input arrays. –  Dec 21 '15 at 07:17
  • @torazaburo _"I think, but am not sure, that he wants values in all the input arrays."_ Yes, `js` at post should filter results from all input arrays – guest271314 Dec 21 '15 at 07:20