Nested if statements - how to refactor conditions to use one if statement while iterating

问题: I have an array of objects: var contacts = [ { "firstName": "Akira", "lastName": "Laine", "number": "0543236543", "likes": ["Pizza", "Coding", "...

问题:

I have an array of objects:

var 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"]
  }
];

I have written a function to iterate over them.

function lookUpProfile(name, prop) {  

  for ( i = 0; i < contacts.length; i++) {  
    if (contacts[i].firstName === name) {  
      if (contacts[i].hasOwnProperty(prop)) {                     
        return contacts[i][prop]     
      } else {
        return "No such property"; 
      }
    }
  }

return "No such contact"; 

}

console.log(lookUpProfile("Harry", "likes"));

I have attempted to restructure the function, so rather than using two nested if statements I can use one if statement and the AND operator. Why have I done this to improve readability as I find nested ifs hard to read. When I do though I don't get the expected return value - I get 'No such property' when the object definitely exists. So in other words why is the second function not working correctly? have I broken the way the function iterates?

function lookUpProfile(name, prop) {  

  for (var i = 0; i < contacts.length; i++) {  
    if (contacts[i].firstName === name && contacts[i].hasOwnProperty(prop)) {                 
      return contacts[i][prop]     
    } else {
      return "No such property"; 
    }
  }
  return "No such contact"; 
}

console.log(lookUpProfile("Harry", "likes")); 

回答1:

The problem here is that you're trying to do 2 things.

  1. Find the right object from an array by first name.
  2. Determine if that object has a specific property, and if so return its value.

For that, having a nested if makes sense. 2 bits of logic, 2 if statements.

You can of course refactor it to not use nested ifs, first try to find the object and then check if that object has the required property:

function lookUpProfile(name, prop) {  
  var obj = contacts.find(c => c.firstName === name);
  if(!obj)
     return "No such contact";
     
  if(obj.hasOwnProperty(prop))
     return obj[prop];
     
  return "No such property";
}


var 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"]
  }
];


console.log(lookUpProfile("Harry", "likes")); 
console.log(lookUpProfile("Harry", "foo")); 
console.log(lookUpProfile("Bob", "likes")); 


回答2:

The second function doesn't work properly because here:

for (var i = 0; i < contacts.length; i++) {  
    if (contacts[i].firstName === name && contacts[i].hasOwnProperty(prop)) {                 
      return contacts[i][prop]     
    } else {
      return "No such property"; 
    }
  }

if the first if fails, the code return. So, because it returns, whenever an item fails the if no other items are processed, which is different from your first function.

If you want to keep your current for loop and just return whether the contact exists or not (and an eventual property), you can keep track whether the contact exists or not and return the error accordingly.

Please note that this can be accomplished in shorter and better ways, I'm just trying to keep your code as it currently is, without the double if statement you don't want to use.

var 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) {  
  var contactExists = false;
  for (var i = 0; i < contacts.length; i++) {
    contactExists = contactExists || contacts[i].firstName === name;
    if (contactExists && contacts[i].hasOwnProperty(prop)) {                 
      return contacts[i][prop];
    }
  }
  return contactExists ? "No such property" : "No such contact"; 
}

console.log(lookUpProfile("Harry", "likes")); 
console.log(lookUpProfile("Akira", "S")); 
console.log(lookUpProfile("Nothing", "Hey"));


回答3:

Your issue with your second code example is that no matter what your condition in your if statement evaluates to, you will always return from your function, thus stopping the loop after its first iteration.

If you wish to not have nested statements, you can create a variable, which defines what message you'll return (ie: whether you should return "No such property" or "No such contact") once your loop is complete. Thus, instead of returning from within your loop, you can change the variable within the loop such that it represents what you will return once it is complete.

For example:

function lookUpProfile(name, prop) {  
  let msg = "No such contact"; // default message
  for (let i = 0; i < contacts.length; i++) {
    let hasProp = contacts[i].hasOwnProperty(prop);
    if (contacts[i].firstName === name && hasProp) {                 
      return contacts[i][prop];     
    } else if(!hasProp) {
      msg = "No such property"; // change the value of the message to return
    }
  }
  return msg; // if we reach this point, we must've not returned earlier in the loop, so we can output the message
}

console.log(lookUpProfile("Harry", "likes"));

However, by using nested if statements, you'll allow your code to return earlier than the above code (ie: the entire loop doesn't need to run) thus making your code more efficient.


回答4:

Here's a very compact and readable code using Array.find, using roughly the same number of checks as in your first implementation (code simplified for readability):

const arr = [
  {name: 'john', age: 42},
  {name: 'doe', age: 24},
];

const lookup = (name, prop) => {
  let found = arr.find(elt => elt.name==name);
  if (!found) return 'No such name';
  if (found && !found.hasOwnProperty(prop)) return 'No such property';
  return found;
}

console.log(
lookup('john', 'age'),
lookup('john', 'size'),
lookup('joe', 'age'),
lookup('joe', 'size'),
);


回答5:

Do it like this. The problem in your code is, you return immediately if first item does not matched the condition.

function lookUpProfile(name, prop) {  
  for ( i = 0; i < contacts.length; i++) {  
    if (contacts[i].firstName === name && contacts[i].hasOwnProperty(prop)) {
      return contacts[i][prop];   
    }        
  }

  return "No such contact or property under contact";    
}

回答6:

Just in case, a single line of code might do the trick:

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"]}];

const lookup = (arr, name, prop) => (match = arr.find(record => record.firstName == name), !match ? 'No such record' : !match[prop] ? 'No such prop' : match[prop]);

console.log(lookup(contacts, 'Sherlock', 'lastName'));
console.log(lookup(contacts, 'Sherlock', 'crimeRecords'));
console.log(lookup(contacts, 'Pherlock', 'lastName'));
.as-console-wrapper {min-height: 100%}


回答7:

return statement in function/method stops executing rest of the code from your function. return means - Ok, i find my expected result - so my work is done :)

function lookUpProfile(name, prop) {  

  var result = null;

  for (var i = 0; i < contacts.length; i++) { 

    if (contacts[i].firstName === name && contacts[i].hasOwnProperty(prop)) {                 
      return contacts[i][prop]     
    } else {
      result = "No such property"; 
    }
  }
  return result;
}

For multiple matching:

function lookUpProfile(name, prop) {  

  var result = [];

  for (var i = 0; i < contacts.length; i++) { 

    if (contacts[i].firstName === name && contacts[i].hasOwnProperty(prop)) {                 
      result.push(contacts[i][prop]);     
    } else if(result == []) {
      result = "No such property"; 
    }
  }
  return result;
}

console.log(lookUpProfile("Harry", "likes"))
  • 发表于 2019-07-07 15:08
  • 阅读 ( 189 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除