Sorted Union using .filter()

I completed the challenge just fine using forEach. But I want to get more familiar with higher order functions.
Am trying to do redo this with .filter() method. Below is the code, I don’t know what am I missing. It’s like it’s not even filtering. The same array is returned.


function uniteUnique(arr) {
  let newArray = [];

  var args = Array.prototype.slice.call(arguments);

  args.forEach(function(val){
      newArray = newArray.concat(val);
      console.log(newArray);
  })

  let finalArray = [];

  finalArray = newArray.filter( val => finalArray.indexOf(val) === -1);
  
  return finalArray;
}

uniteUnique([1, 2, 3], [5, 2, 1]);

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36.

Link to the challenge:
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sorted-union

isn’t finalArray an empty array when you call filter?
perhaps you should be using the ‘arr’ instead?

Yes, it is. But filter is called on newArray.
And if I replace finalArray with arr, I can’t check with .indexOf because the elements will already be in it and won’t be equal to a negative value.

filter returns a new array. But it needs an existing array to filter it…

hope that makes sense.

read here for more info:

I understand what you’re saying. The array filter is called on must not be empty. Am calling it on newArray, which is not empty.
Hope this makes sense.

@graven_whismas, it’s true that your calling filter on newArray; however, what are you calling indexOf on?

1 Like

What am thinking is - Since the finalArray is empty, if I check finalArray.indexOf(Any element) It must equal -1, unless element is already in it. Once an element say, 5 is inside the finalArray, the next time 5 comes around as an element, indexOf will equal a positive value, so it won’t enter the finalArray.
I know am wrong somewhere, but I need an explanation.

two concerns with your thinking:

1- how does the element get inside finalArray? (what code puts elements in there? Is the code running before the filter runs?)

2- filter is very simple. It filters something. That is , you give it something (an array of values) and it gives you a new array which is either exactly the same or smaller (a subset of that array). So do you need to filter something? What is the condition that you are using to filter each item in newArray? If the condition is , does this item in newArray exist in finalArray, the answer will always be “no it doesn’t exist since finalArray is empty”.

Right this seems to be a miss understanding of how filter works.

Just realize that filter doesn’t necessarily alter ‘finalArray’ on every iteration (it doesn’t necessarily push items to finalArray). Rather, at the very end, it will give you an new array that satisfies the predicate function condition.

In computer science terms, filter doesn’t have the “side effect” of pushing to the array.

if your filter function , on the other hand, adds something to finalArray… then that is a different story.

There is a lot to process in my last reply, so let me see if I can clarify.

filter doesn’t work like this

const newArray = [...allYourIntegers]
const finalArray = [];
const condition = function someRandomConditionToRun

for (let i = 0; i < newArray.length; i++) {
  const integer = newArray[i];
  if (someRandomConditionToRun(integer)) {
    finalArray.push(integer)
  }
}

I don’t know if that’s all that accurate @hbar1st

I’ll try answering your questions:
I have one, though. Am assuming filter returns an array(it does, right?), so am assigning it to finalArray, which at the moment is empty.

  • How does the element get inside the array? Am filtering newArray, which returns a filtered array, stored in finalArray.
    -Do I need to filter something?
    Yes. newArray has repeated elements. If I enter them in order into the finalArray, they’ll have an index value, and if the same element is reentering, it can’t. Cause I am testing if every element that enters finalArray is not already in it, using .indexOf(elem). Now take this short code, for example.
let arr = [];
let a = 10;
console.log(arr.indexOf(a));
if(arr.indexOf(a)===-1){
  arr.push(a);
  }
  console.log(arr);

This array will not always be empty; Although it was empty to begin with, but all the same, it will also, like any other array, return -1 if the element isn’t in it. Shouldn’t matter if it’s empty. I’ve tested this code. Only then implemented it.

this is hard to explain… but I’ll try

in your example code , arr doesn’t stay empty because after the condition in the if stmt is done, the arr.push command is executed.

However, in the case of filter, which takes a function, the function only sees an empty array finalArray. Nothing about finalArray (at least in your current code) is changing. It starts out empty and ends up empty.

So finalArray.indexOf(val) will always give -1. always

The only way for that to not happen is if your internal function (for the filter) did something different with finalArray.

Rather the code is doing something roughly like this.

const newArray = [...allYourIntegers]
const finalArray = [];

function filter(someRandomConditionToRun) {
  const arrayThatFilterReturns = []
  const condition = function someRandomConditionToRun

  for (let i = 0; i < newArray.length; i++) {
    const integer = newArray[i];
    if (someRandomConditionToRun(integer)) {
      arrayThatFilterReturns.push(integer)
    }
  }
  return arrayThatFilterReturns;
}

At the very end you’re assigning the variable finalArray to the arrayThatFilterReturns

I can suggest something to you Graven, which is to take your internal function and add some logging to it (console.log) then open the browser console to see what you get (make the internal function log the result for eg of the if statement and the length of the array).

I understand what you guys are trying to explain.
This filter method I used before has worked, take a look:

newArray.filter(val => {
    if(finalArray.indexOf(val)===-1){
      finalArray.push(val);
    }
  })
return finalArray;

Right now, @Ethanefung, I’d like to know why the last bit of code you wrote is wrong.
I’ve done the challenge two ways already, cus I don’t wish to rush to completion. I’d like to know of ways I can do this.

yup, that’s what I was indicating. So, given that you know this, what concern is left?

Using filter in that way is bad practice and bad form

Filter is meant to be an abstraction that returns a new array with only selected elements in it, it should never mutate the state of another thing, that’s so far removed from it’s purpose

If you want to do what you’ve done there, use a foreach

Similarly with map and reduce, their functions are supposed to be pure functions and ought to never mutate some state elsewhere

Breaking the abstraction and using it essentially as a glorified for loop is bad

1 Like

My concern is:

const result = words.filter(word => word.length > 6);

That’s from the documentation of filter();
You give a condition, if the condition is true, the value enters the filtered array. Right? I gave it a condition, if it’s true, it must filter the value into a new array. Am just repeating myself.

let finalArray = [];

  finalArray = newArray.filter( val => finalArray.indexOf(val) === -1);
  

mkay, well this is an interesting way of using filter.

If you recall, that filter should be returning an array.

If you run

console.log(newArray.filter(val => {
    if(finalArray.indexOf(val)===-1){
      finalArray.push(val);
    }
  }));

you’ll notice that your filter doesn’t return an array anymore.
instead your filter function is just running the side-effect of mutating finalArray.

1 Like