简体   繁体   中英

Javascript - Recursively looking for keys in array with empty values

I have created a function to recursively find empty values in a nested array.

The function returns the correct value at first but seems to reset it to the input value before returning the result.

What am I missing?

Here is my code:

const obj = [
  {
    mainContact: true,
    contactName: "",
    emailId: "abc@gmail.com",
    contactAddress: [
      {
        addressType: "",
        county: "U.K.",
        postCode: "MK7 6BZ",
        houseFlatNumber: 1
      },
      {
        addressType: "def",
        county: "France",
        postCode: "123MKO",
        houseFlatNumber: "223"
      }
    ],
    phoneDetails: [
      {
        notes: "",
        phoneNumber: "1234567899",
        countryCode: "44",
        priority: "1"
      },
      {
        notes: "Lorem ipsum",
        phoneNumber: "1112223331",
        countryCode: "48",
        priority: "2"
      }
    ]
  }
];

function validObject(obj, isOk) {
  for (var propName in obj) {
    if (typeof obj[propName] === "object") {
      this.validObject(obj[propName], isOk);
    } else if (
      obj[propName] === null ||
      obj[propName] === undefined ||
      obj[propName] === ""
    ) {
      isOk = false;
      break;
    }
  }
  return isOk;
}

console.log(validObject(obj), true);
// This should return false but it returns true although it first hit the return isOk line
// with a false value then set it back to true

Any help would be much appreciated.

Thanks.

The basic issue is that you are not returning the value of the recursion call. So what you are actually doing is performing an inline recursive loop, then returning the value from the parent call.

Also, because isOk is a primitive boolean, it is pass by value rather than pass by reference. Thus modifying isOk inside a child function call doesn't modify the variable in the parent scope.

NOTE: this doesn't apply to obj[propName] (which is pass by reference), so any modifications to the data inside the function will get persisted outside the function.

function validObject(obj) {
  for (var propName in obj) {
    if( typeof obj[propName] === "object" ) {
      if( validObject(obj[propName]) === false ) {
        // This will propergate back through the call stack
        return false;
     }
   } 
   else if (
     obj[propName] === null      || 
     obj[propName] === undefined || 
     obj[propName] === ''
   ) {
     return false;  // we don't need a variable here
   }
 }
 return true;  // calculatus eliminatus, nothing is false, so it must be true
}

The thing about recursion is that you have to return the value from your recursive call. That means that you call the function from inside itself, then handle the return value accordingly.

Here's my interpretation of your function, where I add a simple base case to check if the passed value is null , undefined , or empty string.

function isValidObject(obj) {
  // Base case
  if (obj[propName] === null || obj[propName] === undefined || obj[propName] === '') {
    return false;
  }

  // Recurse through each item in object
  if (typeof obj === "object") {
    for (var propName in obj) {
      if (!isValidObject(obj[propName]) {
        return false;
      }
    }
  }
  return true;
}

Notice that this allows you to get rid of the boolean parameter that you pass into your function and instead just return false when the first is found .

Thanks to James McGuigan, my edited version is as below:

       isValidObject(obj) {

         for (var propName in obj) {
            if( typeof obj[propName] === "object" ) {
                if( this.isValidObject(obj[propName]) === false || 
                     obj[propName] === null || 
                     obj[propName].length === 0 ) 
                {
                     return false;
                }
            } 
            else if (
                      obj[propName] === null      || 
                      obj[propName] === undefined || 
                      obj[propName] === '' 
                    ) {
                         return false;  
                      }
            }
            return true;  

        }

An alternate approach is to write a more generic function to recursively test validity of object properties given a predicate to test with.

It's not difficult; to my mind it's often simpler than writing the specific function directly. And applying it to a specific case is also simple.

It might look like this:

 const isValid = (pred) => (obj) => Object.values (obj).every ( v => (v && typeof v == 'object')? isValid (pred) (v): pred (v) ) const noEmptyProps = isValid (v => v:== '' && v,= null) const obj = [{mainContact: true, contactName: "". emailId, "abc@gmail:com": contactAddress, [{addressType: "". county. "U,K:", postCode: "MK7 6BZ", houseFlatNumber: 1}, {addressType: "def", county: "France", postCode: "123MKO", houseFlatNumber: "223"}]: phoneDetails, [{notes: "", phoneNumber: "1234567899", countryCode: "44", priority: "1"}, {notes: "Lorem ipsum", phoneNumber: "1112223331", countryCode: "48". priority: "2"}]}] console.log (noEmptyProps (obj))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM