user6398538
user6398538

Reputation:

Get item what's not on the 2nd, 3rd arrays - JS

I am trying to create a function that will get the items that cannot be seen on the 2nd or 3rd and upcoming arrays passed within the function.

Right now my function gets only the similar items. How can I make it get the difference (w/c are the items that doesn't exist to the 2nd and 3rd and proceeding arrays.

const callM = function(arrays) {
   arrays = Array.prototype.slice.call(arguments);


   let result = [];

  for(let i = 1; i < arrays.length; i++){
    for(let x = 0; x < arrays[i].length; x++){
      if(arrays[0].includes(arrays[i][x])){
        result.push(arrays[i][x]);
      }
    }
  }

  return result;

  };

console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]

The logic right now is a bit off as it gets the opposite. How do i fix this?

Also is there a way to do this using Higher Order functions such as reduce or filter?

Thanks!

Upvotes: 3

Views: 74

Answers (5)

Ty Kroll
Ty Kroll

Reputation: 1395

I'd think about this differently. As the difference between two sets: array 0 and array 1...n

To get array 0, just shift it off the top

const arr0 = arrays.shift()

Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift

This removes the first array from arrays Next we combine the remaining arrays

const arrN = arrays.reduce(function(prev, curr) {
  return prev.concat(curr)
})

Ref: http://www.jstips.co/en/javascript/flattening-multidimensional-arrays-in-javascript/

Unneeded, handled by includes as mentioned by @Phil

Next filter duplicates from arrN by comparing with itself

const unique = arrN.filter(function(elem, index, self) {
    return index == self.indexOf(elem);
})

Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Next filter with includes to find the difference (or union)

const diff = arr0.filter(function(item) {
    return !arrN.includes(item))
}

Full snippet:

function callM(arrays) {

  const arr0 = arrays.shift()
  const arrN = arrays.reduce(function(prev, curr) {
    return prev.concat(curr)
  })
  //const unique = arrN.filter(function(elem, index, self) {
  //    return index == self.indexOf(elem)
  //})
  return arr0.filter(function(item) {
    return !arrN.includes(item)
  })
}

console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10]]))
console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]]))

of course ES6 would be easier. ;)

const callM = (first, ...rest) => {
  const arrays = [].concat(...rest)
  return first.filter(item => !arrays.includes(item))
}

console.log(callM([1, 2, 3, 4, 5], [5, 2, 10]))
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))

Upvotes: 1

le_m
le_m

Reputation: 20238

A short solution for small and medium sized arrays:

// Return elements in array but not in filters:
function difference(array, ...filters) {
  return array.filter(el => !filters.some(filter => filter.includes(el)));
}

// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 1, 10], [7, 2, 8])); // [3, 4]

For large inputs, consider creating a Set from all filters and filtering in linear time using set.has(el).

In order to fix your implementation, you could label the outer for-loop and continue from there whenever a filter contains one of the array elements. Only when all filters pass without match, you push the array element into the result:

// Return elements in array but not in filters:
function difference(array, ...filters) {
  const result = [];

  loop: for (const el of array) {
    for (const filter of filters) {
      if (filter.includes(el)) continue loop;
    }
    result.push(el);
  }
  return result;
}

// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // [3,4]

Upvotes: 1

Slai
Slai

Reputation: 22876

The "proper" way to exclude values is usually to use a lookup hash set with the values to exclude:

const callM = (a, ...b) => (b = new Set(b.concat.apply(...b)), a.filter(v => !b.has(v)))

console.log(callM([1, 2, 3, 4, 5], [5, 2, 10]));              // [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]));   // [3, 4]

Upvotes: 0

Hassan Imam
Hassan Imam

Reputation: 22564

You can use array#reduce to create object lookup of all the other array excluding the first array. Then use array#filter to get the values which are not present in the object lookup

var callM = (first, ...rest) => {  
  var combined = rest
                  .reduce((res,arr) => res.concat(arr))
                  .reduce((o, v) => {
                    o[v] = true;
                    return o;
                  },{}); 
  
  return first
          .filter(v => !combined[v]);
}

console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]

Upvotes: 0

ACOMIT001
ACOMIT001

Reputation: 510

If you're willing to use Underscore, you can do this in one line of code:

console.log(_.difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))

https://jsfiddle.net/o1zuaa6m/

Upvotes: 0

Related Questions