(how to NOT) use destructuring in a function to match 2 objects

This part of epic quest to solve this.

comparator say which object should be excluded in a comparison. I want to put that value into a destructuring expression that assign the remaining values (those that are included in the 2 objects). BUT when I put the result of the function into the expression I get an error (that var is already declared). How can I fix the destructuring expression?

const manyKeys = {
  item1: { name: 'sd', value:'s' },
  item2: { name: 'sd', value:'s' },
  item3: { name: 'sd', value:'s' }
};

const allowed = ['item1', 'item3'];

function comparator (inputObj, testObj){
  let excluded = [];
  Object.keys(inputObj).forEach(function(item){
  if (testObj.indexOf(item) == -1) {
  excluded.push(item)
  }
})
return excluded; //--> [ 'item2' ]
}


const {item2, ...filteredObj} =  manyKeys // can't use excluded value ([ 'item2' ]) instead of item2 cause it has already been declared

console.log(filteredObj); //-->should be   {  item1: { name: 'sd', value:'s' }, item3: { name: 'sd', value:'s' }

per-se, the code should run and work fine, but you are declaring a lot of global variables.

This may work fine on the first execution, but on each sub-sequential run the interpreter will complain that some of the constants have already been declared.

And that’s precisely what FCC test suite does, it runs your code multiple times testing various output.

How would you avoid this “global” variables issues?
Hope this helps :slight_smile:

1 Like

Thanks, It is useful indeed :slightly_smiling_face: . But I have a question: in the filteredObj context excluded becomes a global var?

Now, to try to respond to you: I’d say that get in const {item2, ...filteredObj} = manyKeys into the function would help (to avoid global vars)… am I right or lost?

By the way, I running this in vscode.

No, since it’ s declared inside the function.
However I don’t see any calls made to that function :slight_smile:

The issues with the others is that they are declared in the global scopes, so they may cause issues.
And yes, having them wrapped by an IIFE or a regular function should do.

Hope this helps :+1:

You’re right :sweat_smile: . Please, let start over (I feel stuck in this :weary:):

My goal is write a function that take 2 objects, compare, and determine whether there is a non-included object, AND pass that excluded value into a destructuring statement, to return filteredObj . It goes like this:

function comparator (inputObj, testObj){
  let excluded = [];
  Object.keys(inputObj).forEach(function(item){
  if (testObj.indexOf(item) == -1) {
  excluded.push(item)
  return excluded; //--> [ 'item2' ]
  }
})
const {excluded, ...filteredObj} =  inputObj //PROBLEM IS HERE: if a delete excluded and put 'item2' comparator works
return filteredObj;
}
//
const manyKeys = {
  item1: { name: 'sd', value:'s' },
  item2: { name: 'sd', value:'s' },
  item3: { name: 'sd', value:'s' }
};

const allowed = ['item1', 'item3'];
//

comparator(manyKeys, allowed); 

My expected output, since I’m calling comparator over manyKeys and allowed is:

 { item1: { name: 'sd', value: 's' },  item3: { name: 'sd', value: 's' } }

InsteadI’m getting the error:

Identifier 'excluded' has already been declared

the problem that I want to to solve is: in the context of my function how can I use the value of excluded in the destructuring assignment As I commeted in the code if I delete excluded and put ‘item2’ my function return what I want for this particular objectcs, BUT the function should do the same with any pair of objects (with the same structure of manyKeys and allowed.), and that’s not possible if I can’t use THE VALUE in excluded.

I think you’re overcomplicating the overall task a lot here, but this stuff is hard, so to focus on the function you’ve written:

So you have a function that takes an object and an array of strings (allowed keys). You’ve called that “allowed” so I’ll change the name as testObj is very confusing here:

function comparator (inputObj, allowed) {
  let excluded = [];

You iterate over the keys of the input object, in your example that gives you ["item1", "item2", "item3"]. If the key is not in allowedKeys, push it to the excluded array:

  Object.keys(inputObj).forEach(function(item){
    if (allowed.indexOf(item) == -1) {
      excluded.push(item)

for some reason you return here, this isn’t how forEach works (it’s for side effects) but it won’t do anything at all so no harm done

    return excluded; //--> [ 'item2' ]
    }
  });

So now you have an array called excluded which has “item2” in it. And you try to destructure the input object. Which looks like {item1, item2, item3}. It doesn’t have a key called excluded, so you can’t extract that. And you can’t assign it to a variable called excluded, because you’ve already used that. And the second part doesn’t make sense, because it’s all the properties from filteredObj, and you can’t assign those that way

  const {excluded, ...filteredObj} = inputObj;

This is equivalent to writing

const excluded = inputObj.excluded; // this does not exist
const ...filteredObj = inputObj; // this is not valid syntax at all, so cannot work

As both of these variable names have already been declared, this blows up.


Destructuring means you can extract items from an object and assign them to variables in one operation.

const myObj = {foo: 1, bar: 2 }
const { foo, bar} = myObj;

This is exactly equivalent to writing

const myObj = {foo: 1, bar: 2 }
const foo = myObj.foo;
const  bar = myObj.bar;

It doesn’t do any special filtering or anything, it is just a very useful shorthand. You are assigning to variables, which is why you get the error

Identifier 'excluded' has already been declared

Because you’re trying to repeatedly declare variables with the same name, which you can’t do

1 Like

Great analysis and explanations Dan!
Thank you very much

This one might be better:

function comparator (inputObj, testObj){
  Object.keys(inputObj).forEach(function(item){
  if (testObj.indexOf(item) == -1) {  
  delete inputObj[item];
  }
})
return inputObj;
}
//
const manyKeys = {
  item1: { name: 'sd', value:'s' },
  item2: { name: 'sd', value:'s' },
  item3: { name: 'sd', value:'s' }
};

const allowed = ['item1', 'item3'];


console.log(comparator(manyKeys, allowed)); //--> {  item1: { name: 'sd', value:'s' }, item3: { name: 'sd', value:'s' }

A bit late to the party, but if I may, your comparator function is performing a side-effect on the object passed as parameter, or to put is simply it’s changing its output.

This is a common source of bugs and general headache, and in general is better to avoid such thing.

If I were to run your code:

let filteredObject = comparator(manyKeys, allowed);

And wanted to test, compare, or if some other portion of your code depends on manyKeys it would cause troubles since the object has changed after comparator has run.

Hope this won’t complicate things too much. :slight_smile:

1 Like

Not at all. It’s great to consider things like that. I’m going to bed, but let me mull over and code a new (inmutable) version (if I got this right!).
:sleeping:

I guess this work:

// return allowed values: compare multidimentional object to array

function comparator (inputObj, testObj){
  const {...mutatedCopy} = inputObj;
  
  Object.keys(mutatedCopy).forEach(function(item){
  if (testObj.indexOf(item) == -1) {  
  delete mutatedCopy[item];
  }
})
return mutatedCopy;
}
//
const manyKeys = {
  item1: { name: 'sd', value:'s' },
  item2: { name: 'sd', value:'s' },
  item3: { name: 'sd', value:'s' }
};

const allowed = ['item1', 'item3'];


comparator(manyKeys, allowed); //--> {  item1: { name: 'sd', value:'s' }, item3: { name: 'sd', value:'s' }. It doesn't return this because the error: excluded has already been declared