Profile Lookup --> I'm so close -- I found where the failure occurs but I don't know what to do about it

I took a little different approach to solving this challenge. I had tried several other approaches over the course of an afternoon but none of them were as promising as the one I am currently pursuing (I hope it doesn’t make your eyes hurt too bad). I’ve narrowed down the source of the problem to line 43 in onecompiler (the return statement that is not executing). I’ve spent the last several hours trying to discover why this return statement is not executing and I spent almost 2 hours on irc #programming last night - they kept giving me ALL these hints but I’m just not grasping the reason for the failure. I feel as thought I could spend the rest of my life guessing and never figure it out.

Yes, I would like to explore other approaches BUT not before solving the problem using THIS approach I’m on…

You can see the code (with debugging lines) in code in oencompiler ← this reveals A LOT about what is actually happening in that code and just how close it seems to be to working.

Here is my code so far without the debugging lines…

// Setup
const contacts = [
  {
    firstName: "Akira",
    lastName: "Laine",
    number: "0543236543",
    likes: ["Pizza", "Coding", "Brownie Points"],
  },
  {
    firstName: "Harry",
    lastName: "Potter",
    number: "0994372684",
    likes: ["Hogwarts", "Magic", "Hagrid"],
  },
  {
    firstName: "Sherlock",
    lastName: "Holmes",
    number: "0487345643",
    likes: ["Intriguing Cases", "Violin"],
  },
  {
    firstName: "Kristian",
    lastName: "Vos",
    number: "unknown",
    likes: ["JavaScript", "Gaming", "Foxes"],
  },
];

function lookUpProfile(name, prop) {
  // Only change code below this line

  if ( typeof lookUpProfile.i == 'undefined' ) {
      lookUpProfile.i = 0;
  }

  if (name == contacts[lookUpProfile.i].firstName && contacts[lookUpProfile.i].hasOwnProperty(prop)) {
    return contacts[lookUpProfile.i][prop];  // THIS IS THE LINE THAT IS SHOWING UP AS "undefined"  -->  IT IS SOMEHOW NOT GETTING THE RPOPERTY AND RETURNING IT  -->  IDK WHAT THE PROBLEM IS OR HOW TO FIX IT
  } else if (lookUpProfile.i < contacts.length) {
    ++lookUpProfile.i;
    lookUpProfile(name, prop);
  }

  // Only change code above this line
}

console.log(lookUpProfile("Sherlock", "likes"));
// console.log(lookUpProfile("Kristian", "likes"));


Line 37 (here / above) seems to be the problem but why? And how to fix it?

Note that when function is called with “Akira” and “likes”

console.log(lookUpProfile(“Akira”, “likes”));

When it is called as shown above it returns [“Pizza”, “Coding”, “Brownie Points”] just fine and it
has to execute the same block of code - specifically - line 37 (line 43 in onecompiler) in order to do so.

To sum it all up…

if the function is called with the name corresponding to the name in the first object it works fine.
If it is called with any name corresponding to a name in subsequent objects it fails with ‘undefined’

I realize that there are additional requirements that are not being addressed in this code but I want to get this part working first and then add the solutions for the additional requirements to the code after that.

Thanks for any help

// Setup
const contacts = [
{
firstName: “Akira”,
lastName: “Laine”,
number: “0543236543”,
likes: [“Pizza”, “Coding”, “Brownie Points”],
},
{
firstName: “Harry”,
lastName: “Potter”,
number: “0994372684”,
likes: [“Hogwarts”, “Magic”, “Hagrid”],
},
{
firstName: “Sherlock”,
lastName: “Holmes”,
number: “0487345643”,
likes: [“Intriguing Cases”, “Violin”],
},
{
firstName: “Kristian”,
lastName: “Vos”,
number: “unknown”,
likes: [“JavaScript”, “Gaming”, “Foxes”],
},
];

function lookUpProfile(name, prop) {
// Only change code below this line
if ( typeof lookUpProfile.i == ‘undefined’ ) {
lookUpProfile.i = 0;
}

if (name == contacts[lookUpProfile.i].firstName && contacts[lookUpProfile.i].hasOwnProperty(prop)) {
return contacts[lookUpProfile.i][prop]; // THIS IS THE LINE THAT IS SHOWING UP AS “undefined” → IT IS SOMEHOW NOT GETTING THE RPOPERTY AND RETURNING IT → IDK WHAT TO DO TO FIX IT
} else if (lookUpProfile.i < contacts.length) {
++lookUpProfile.i;
lookUpProfile(name, prop);
}

// Only change code above this line
}

lookUpProfile(“Akira”, “likes”);





      **Your browser information:**

User Agent is: <code>Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0</code>

**Challenge:**  Profile Lookup

**Link to the challenge:**
https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-javascript/profile-lookup

Dot notation won’t work here. Bracket notation is mandatory when the property name is in a variable. Bracket notation is also mandatory for accessing array entries.

I don’t understand what is happening in your code. Maybe you could explain.
Also the above is not helping, as far as I can tell you nowhere change the variables name and prop. So the recursion does the same thing over and over. I may be mistaken.

  if ( typeof lookUpProfile.i == 'undefined' ) {
      lookUpProfile.i = 0;
  }

The above code is (supposed to be) creating a static variable to use as a counter and to index the array named contacts (ie: its use is to iterate through each of the array elements). lookUpProfile.i adds a variable to the function lookUpProfile named i

The index variable lookUpProfile.i that is found in the line

if (name == contacts[lookUpProfile.i].firstName && contacts[lookUpProfile.i].hasOwnProperty(prop))

Is what makes the next array entry be what gets compared. It is incremented at ++lookUpProfile.i (after the current comparison is done but before the function is fired again.

The function is fired again at lookUpProfile(name, prop); (right after the index variable is incremented). The (name, prop) values are not changed because they are not supposed to change - those arguments are what is being looked for (if it exists) and what is being looked for does not change - what you are comparing against (the elements of the array) is what has to change.

Lets walk through the code…

/* If it does not exist then create a variable named i (a static variable) */

  if ( typeof lookUpProfile.i == 'undefined' ) {
      lookUpProfile.i = 0;
  }
/* Check the element of the array indicated by the variable i to see if we have found what we are looking for (this block of code - the entire if block - is equal to the stop condition for the recursion). If the conditions are both true (ie: the name we are looking for is found and the property we want is there) then the line inside that conditional should return the value of the property that was requested and the program ends. */

if (name == contacts[lookUpProfile.i].firstName && contacts[lookUpProfile.i].hasOwnProperty(prop)) {
    return contacts[lookUpProfile.i][prop]; 
  }
/* If we have not found what we are looking for (ie: the if conditions are not met above) then we use else if to make sure we aren't at the end of the array. If we aren't at the end of the array we increment i (the variable being used to index the array). Incrementing i is what allows us to iterate to the next element of the array (ie: iterate over the array). After that we fire that function again (in other words the function calls itself) - this is the recursion like behavior that the function calls itself from within itself.  Note that once the function is fired again the index variable i has already been incremented. That's how the array is being crawled over. */

else if (lookUpProfile.i < contacts.length) {
    ++lookUpProfile.i;
    lookUpProfile(name, prop);
  }
/* Now that the function is called again  (at the line shown below) we start again at the top. The first if condition checks whether the variable i exists. Since it does exist the condition fails and the code in that block is not executed. Then we check again to see if we have found what we are looking for (the second if condition). If we have found it and the property exists the code inside the condition is supposed to return what we want (the value of the property asked for). If we have not found what we are looking for (the condition fails) then we again check to see if we are at the end of the array. If we are not at the end of the array we again increment i and we again fire the function (the algorithm repeats / continues).  */

lookUpProfile(name, prop);

Based on the above this program should iterate over the contacts array, checking to see if what we want is found at each array element and either return what we want; or, if what we want is not in there, exit the program.

1 Like

That’s a terrible idea. Don’t do that.

You have have a very complex and buggy approach to this problem.

There is no need to make this bizzare .i property. There is no need for recursion.

But the faux-recursion you have written has no base case, which is why it overruns the length of the array. It should be fixable if you really, really want to do this.

If you are trying to make obfuscated code, then this is good practice at that. Some people enjoy that. C has a big competition for it. But I would reject this approach outright in code review.

The only thing that isn’t working in the code base is the information being looked for not being returned. Why change something that is actually working correctly?

The output of the code (seen at the link shown above) shows that the variable is incrementing as expected.

The output of that link shows what happens when dot notation is not used

I don’t think that variable is doing what you think it is (but it seems to be doing what I intended it to do)

This is a bad approach… You shouldn’t write code like this in any professional software.

That said…

You are running past the end of the array, reaching into undefined values. If you fix this, then you can get this code to pass a test once.

But.

This is a bad problem for recursion. And this is a bad way to implement recursion.

I know what it’s doing now. It still a bad approach.


Also… What happens when you try to call this function again? You have written a function that mutates a global variable and can only be run once. Even if you fix the overrun problem, you will need to fix this issue as well.

Why do people do this?

If they don’t like the approach you took then that is what they go after (even if it could be made to work). I want to learn something from this not just discard it the minute someone says its a bad approach. I would like to learn from it first, before throwing it away. After that I have no problem finding / using a proper approach (but not at the expense of understanding). I’m pretty sure that’s one of the first things I said in my original post in this thread. So don’t people want you to actually learn something? I thought that’s what we do this for. If the only help I get is to tell me its a bad approach and not actually help me learn from it then what has the time and effort helping me really been for?

The arguments being fed to that function Here are data that actually exist in the array and should lead to the information being requested get returned - I did that on purpose.

The output

Happy Path Block

Which comes from line 42 console.log("Happy Path Block"); reveals that that conditional is being enterd when it is supposed to be (at the actual element where the data being looked for is at).

I don’t know how to check whether the program is exiting at that point or continuing on but I would guess that if it continued on I would see additional output instead of undefined and then nothing.

So I want to be clear about something. Are the responses I’m getting supposed to indicate that if I want to try and make this code work (in order to learn something) that no one is going to help me with that?

I’ve told you three things

  1. This is a bad idea

  2. You are overrunning your array

  3. You are causing a change in a global variable that will be a problem

Did you investigate 2 or 3 at all?


It looked like 2 but was actually…

The default return value when none is provided is undefined. Some cases will fall into this.

3 is still an issue though.

More logging:

function lookUpProfile(name, prop) {
  // Only change code below this line

  if ( typeof lookUpProfile.i == 'undefined' ) {
      lookUpProfile.i = 0;
  }

  console.log("i = " + i);
  console.log(contacts[lookUpProfile.i]);

  if (name == contacts[lookUpProfile.i].firstName && contacts[lookUpProfile.i].hasOwnProperty(prop)) {
    console.log("found");
    return contacts[lookUpProfile.i][prop];
  } else if (lookUpProfile.i < contacts.length) {
    console.log("recursion");
    lookUpProfile.i++;
    lookUpProfile(name, prop);
  }
  console.log("fallthrough");
  // Only change code above this line
}

console.log("Test 1");
console.log(lookUpProfile("Sherlock", "likes"));
console.log("Test 2");
console.log(lookUpProfile("Akira", "likes"));

So overrunning isn’t quite right. More ‘falling through’ off the edge of your logic.

  1. This is a bad idea

Probably but it’s still useful for my education (you may KNOW why it is a bad idea but I do not). ← Now I am not talking about the kind of knowledge that comes from someone telling you so; I’m talking about experiential knowledge.

  1. You are overrunning your array

I’m not catching where or how that is occurring. I’ve reached the limit of my understanding when I started this thread. So I keep going back and looking at the code and comparing the output - but I’m just not grasping how or why that is happening.

  1. You are causing a change in a global variable that will be a problem

This is the first I think I’ve seen this information. If it had been said to me earlier I must have missed it. I’ll go back through this thread and look for more detailed information if it was given. I’m wondering what global variable this is so that I can identify what to look for. I ‘guess’ it must be the lookUpProfile.i variable but I wasn’t aware of what the scope of the variable is. I guess I assumed it was a local scope (local to the function) since it is declared inside the function. Again, due to lack of experience. I’m learning.

You’re getting to experience this knowledge right now. Complex recursion = hard to debug and understand. Simple code is better. You understand it, the computer understands it, and future you understands it. Only write fancy code when performance constraints force you to do so.

1 Like

It’s obvious that this code will have bot be scrapped now and I’m sorry it took so long for me to understand that… BUT with the test code you shared with me I’m actually getting to see what is going on and understand the situation (first time seeing information this useful about the code). SO I really want to make sure I understand the output of that test code before I move on → This is the lesson I was looking for (its right in front of me now) but I have to be sure I’m interpreting it correctly.

In the output Here I see two undefined but I think they are each coming from something different. The first undefined in the output seems to be coming from the return statement (line 39 in that code paste). The second undefined seems to be coming from the end of the array being over-run. Is that correct?

===============================

Wait that’s not right. There are two tests (function calls from the global scope) being issued. …

Ok I think I see it. When the program is run more than once (where the program is being initialized from the same file) then the variable i starts back up where it left off.

=================================

If there is a way to make an approach like this work it may not be possible given the constraints of the exercise

// Only change code below this line

// and..

// Only change code above this line

I’m not sure

Yea. 2 and 3 really turned out to be the same thing.

You pushed your iterator variable all the way to the end of the array. But there is no way to ‘loop back’, so you ran out of things to check and were ‘stuck’ on the last element.

The default return value when no return statement has been encountered is undefined, which is what you ended up hitting once you ran to the end of the array.

The function is an object that’s a global variable (or in whatever scope you define the function), so changing i is permanent in that scope until you reset it somehow. But you function has no way of knowing if it’s being called recursively, so there is no way for the function to reset itself.

Generally, if you see a an array, think for loop (or the functional equivalents out there). Loops are how you traverse arrays. It’s what everyone expects, and it’s good to follow conventions unless you really need to break that convention.

You nearly never actually want to use recursion.

But.

Even though it’s a bad fit, you can bully JS into letting this approach work with the use of a default function argument:

(arrow syntax in this challenge, but the name thing works with ‘regular’ function definitions)

A function argument that is a primative value is scoped to the function call, so default values are scoped to the function call as well. Some people love to use default arguments to make recursion work on problems where it isn’t a natural fit.

1 Like

@JeremyLT

Really appreciate you helping me learn so much. I think I’m going to work on that code separately (I’m stubborn and I want to milk it for all the knowledge it’s worth) – but go ahead with something better to pass the challenge with.

Peace
:smile:

1 Like

We got there in the end (I think I was the slower end of the equation tbh). I think if you can get the loop based approach working, then it will be easier for you to get the recursion-with-default-parameters working as well. Ask if you get stuck!

1 Like

Thanks for the explanation. I see what you meant to do. And great conversation as well.
One thing I would say is try to use camelCase for variables. I assume you defined the variable above somewhere outside the function? Otherwise this is a new way to create a global variable for me.

EDIT: is this actually creating an object?

1 Like

@Slimattcode

I’m really not certain how it works. I had looked up how to make a static variable in a function (in js of course). I don’t think it works the way I thought it would though and I got in over my head when the shtf. I may still go back to that codebase (I have it saved and in onecompiler) and try to learn more from it.

The variable (if that’s even what it is) gets defined in the following part of the code…


if ( typeof lookUpProfile.i == 'undefined' ) {
      lookUpProfile.i = 0;
  }

For now I just went with something else and got passed the challenge.

1 Like

This is what I ended up going with…


  const found = contacts.find(contact => (contact.firstName == name));

  if (found) {
    if (found.hasOwnProperty(prop)) {
      return found[prop];
    }
    return "No such property";
  } else {
    return "No such contact";
  }

1 Like

Looks great. Thanks for the explanation. :slightly_smiling_face:

1 Like