6

pretty new to Javascript and I've tried this question about 4 times now in a span of about a month and I am still unable to solve it.

So here is the question: Construct a function intersection that compares input arrays and returns a new array with elements found in all of the inputs. BONUS: Use reduce!

The format is:

function intersection(arrays) {
  // Your Code Goes Here
}

Test Case: Should log [15, 5]

console.log('Extensions 3 Test: ' + intersection([5, 10, 15, 20], [15, 88, 1, 5, 7]/*, [1, 10, 15, 5, 20]*/));

My current solution: Works for the case of only have two items to compare, but not for the third one, I could make it so that I would loop through and compare the obtained values with the next array but I don't think I am on the right path... Also, I am not using reduce to implement it... And I am not sure if I am supposed to be using 'arguments.' Any help is appreciated! Thank you so much.

function intersection(arrays) {
  array = [];
  for (var i = 0; i < arguments.length; i++)
    array.push(arguments[i]);

  var result = [];

  for(var i = 0; i < array.length - 1; i++) {
    for(var j = 0; j < array[i].length; j++) {
      if (array[i+1].includes(array[i][j]))
        result.push(array[i][j]);
    }
  }

  return result;
}
7
  • Why don't you use lodash (or underscore)? Seems crazy to reinvent the wheel for such a well trodden path in JavaScript. Commented Jan 28, 2017 at 23:25
  • @chriskelly: OTOH, why drag one of them in for such a simple function? (And I say that as the author of a similar library myself.) Commented Jan 29, 2017 at 0:51
  • Thanks for all the responses guys! I'll look through them :D Commented Jan 29, 2017 at 2:12
  • @ScottSauyet: I have often written my own lodash-like functions (to eliminated a dependency) only to have people waste time checking it or trying to figure it out what it does and if it does it right, later. if you have _.difference(a, b) in your code it will be far more readable, won't be questioned, and therefore saves time in the long run. Commented Jan 29, 2017 at 15:12
  • @chriskelly: Ah, we have very different experiences. I was not happy with the design or the public API of Underscore and lodash, and ended up starting my own library. So I guess I'm much more willing to reinvent the wheel. "Look, mine's rounder!" :-) Commented Jan 29, 2017 at 20:00

10 Answers 10

6

Although, as several suggestions said, you could use underscore, lodash, or my personal favorite, Ramda (disclaimer: I'm one of the authors), this function should be straightforward enough that you wouldn't even consider a library for it. Here's a simple version:

const intersection = (xs, ys) => xs.filter(x => ys.indexOf(x) > -1);
intersection([5, 10, 15, 20, 3], [15, 88, 3, 1, 5, 7]); //=> [5, 15, 3]

const intersectAll = (...xss) => xss.reduce(intersection);
intersectAll([5, 10, 15, 20, 3], [15, 88, 3, 1, 5, 7],  [1, 10, 15, 5, 20]); //=> [5, 15]

I would think that this is all you need, at least so long as you're worried only about reference/primitive equality and don't need to consider cases where you want to know that {x: 1} and {x: 1} are the same, even though they aren't the same reference. If you do need that, you might look to Ramda's intersection function.

Note that if includes were better supported, I would recommend this version instead, as it reads better:

const intersection = (xs, ys) => xs.filter(x => ys.includes(x));

Also, if you have no need for the binary function, you can make just a variadic version of it by combining the two above:

const intersection = (...xss) => xss.reduce((xs, ys) => xs.filter(x => ys.indexOf(x) > -1));
Sign up to request clarification or add additional context in comments.

Comments

3

Maybe someone will finds it useful.

As an argument to the function you can give any number of arrays of any length and the function is compact, I think ;)

const findSimilar = (...arrays) => {   
  return arrays.reduce((includ, current) =>
    Array.from(new Set(includ.filter((a) => current.includes(a))))
  );
};

console.log(
  findSimilar([5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20])
);

And how it works:
Ok, first u take rest parameters(...arrays) as parameter of function, so u have
arrays = [ [5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20] ]
then in first iteration of reduce we have
includ = [5, 10, 15, 20] and current = [15, 88, 1, 5, 7]
on this two we use filter, what give us [5,15], i use Set to make shure there is no repetition and make array back (Array.from()), which is passed to the next iteration of reduce as "includ", at the next iteration we have
incude = [5,15] and current = [1, 10, 15, 5, 20] and so on ...

We can even use it like this

let result = [
  [5, 10, 15, 20],
  [15, 88, 1, 5, 7],
  [1, 10, 15, 5, 20]
].reduce((includ, current) =>
  Array.from(new Set(includ.filter((a) => current.includes(a))))
);

console.log(result);

2 Comments

Provide some context and explanation to your answer to make is valuable. As well, when answering a 3 year old question with 8 other answers, you may want to provide some justification for answering. What does this do that the other 8 answers have not?
@miken32: Added a small explanation as You mentioned
1

Although not solving your problem directly, you can do what you're trying to do using the opensource library underscore.js.

_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]

You may be able to derive inspiration from the way that's been implemented. The above is the function call to their own _.intersection function which is also dependent on other underscore.js functions as you see below:

  // Produce an array that contains every item shared between all the
  // passed-in arrays.
  _.intersection = function(array) {
    if (array == null) return [];
    var result = [];
    var argsLength = arguments.length;
    for (var i = 0, length = array.length; i < length; i++) {
      var item = array[i];
      if (_.contains(result, item)) continue;
      for (var j = 1; j < argsLength; j++) {
        if (!_.contains(arguments[j], item)) break;
      }
      if (j === argsLength) result.push(item);
    }
    return result;
  };

Comments

1

Here is a solution using reduce, with the empty array passed in as intersection as the initial value.

Iterate the numbers and check if each one appears in one of the subarrays.

If it doesn't, set the Boolean isPresentInAll to false.

If it does appear in all three and it's not already present in the intersection array, then push to the intersection array.

function intersection(arrayOfArrays) {
  return arrayOfArrays.reduce(function(intersection, subArray) {
    subArray.forEach(function(number) {
      var isPresentInAll = true;
      for (var i = 0; i < arrayOfArrays.length; i++) {
        if (arrayOfArrays[i].indexOf(number) === -1) {
          isPresentInAll = false;
        }
      }
      if (isPresentInAll === true && intersection.indexOf(number) === -1) {
        intersection.push(number);
      }
    });
    return intersection;
  }, []);
}

Comments

0

I think i got the right function for you. (Note: results are not sorted!)

var intersection = function() {
    // merge deduped arrays from arguments
    var arrays = Array.prototype.reduce.call(arguments, function(carry, array) {
        return [].concat(carry, array.filter(function(item, index, origin) {
            return origin.indexOf(item) === index;
        }));
    }, []);

    var results = arrays.reduce(function(carry, item, index, arr) {
        if(
            // just select items, which have more then 1 occurance
            arr.filter(function(fItem) {
                return fItem === item;
            }).length > 1 &&
            // ... and which are not already in results
            !~carry.indexOf(item)
        ) {
            carry = [].concat(carry,item);
        }
        return carry;
    }, []);

    return results;
};

Comments

0

Here's a version that uses 2 reduces.

The first iterates the arrays only once to create a hashmap object to track instance counts, the second to return values where counts match number of arguments

function intersection(){
  // convert arguments to array of arrays
  var arrays = [].slice.call(arguments);
  // create an object that tracks counts of instances and is type specific
  // so numbers and strings would not be counted as same
  var counts= arrays.reduce(function(a,c){
     // iterate sub array and count element instances
     c.forEach(function(val){
        var propName =  typeof val + '|' + val;
        // if array value not previously encountered add a new property        
        a[propName] = a[propName] || {count:0, value: val};       
        // increment count for that property
        a[propName].count++;
     });
     return a;
  },{});

  // iterate above object to return array of values where count matches total arrays length
  return Object.keys(counts).reduce(function(resArr, propName){
    if(counts[propName].count === arrays.length){
      resArr.push(counts[propName].value);
    }
    return resArr;
  },[]);
  
}



console.log(intersection([5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20]))

Could use some fine tuning to make sure there are enough arguments and that they are all arrays

2 Comments

Thanks! I think this is what I was looking for! I'll need some time to digest it but thanks for solving the answer using vocabulary that I know :D!
suggest adding some console logging statements inside the loops to get a better feel for what is what. And inspect console.log(counts) also
0

Here's what I came up with using vanilla javascript and one call to reduce.

function intersection(){
 var arrays = [].slice.call(arguments);
 var first = arrays[0];
 var rest = arrays.slice(1);

 return first.reduce(function(all, item, index){  
  var push = rest.every(function(subArray){
      return subArray.indexOf(item) > -1; 
    });
  if(push){
    all.push(item);
   }
  return all; 
 },[])

}

console.log(intersection([5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20]));

Comments

0
function intersection(arrays) {
  let common = arrays.reduce(function(accumulator, currentValue) {
    return accumulator.filter(function(x){
      return currentValue.indexOf(x) > -1;
    })
  })
  return common;
}

Comments

0

To optimize your answer that couldn't work on more than 2 subarrays and didn't use reduce, here's the code that works for however many subarrays you pass in.

function intersection(arr1, arr2, arr3){
  let ans = arr1[0]; // ans = [5,10,15,20]
  for(let i = 0; i < ans.length; i++){ // i = 0...3
    for(let j = 1; j < arr1.length; j++){ // j = 1...2
      if(!(arr1[j].includes(ans[i]))){ // if the new subarray doesn't include an element in the ans
        ans.splice(i, 1); // delete the element from ans
        }
    }
  }
  return ans;
}

const arr1 = [5, 10, 15, 20];
const arr2 = [15, 88, 1, 5, 7];
const arr3 = [1, 10, 15, 5, 20];
console.log(intersection([arr1, arr2, arr3])); // should log: [5, 15]

Comments

0

You can try this:

const arr = [[1,2,3,4], [2,3,4,5], [3,4,5,6]];

// expected output should be [3,4]
// we can use reduce like this:

 function common (arr) {
   return arr.reduce ((a,b) => {
     return a.filter(item => b.includes(item))
   })
}

console.log(common(arr));  

// should give you [3,4]

1 Comment

There is a missing ) after b.includes(item). Edit button is currently buggy in review, I can't make the edit myself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.