1
\$\begingroup\$

I have a modal for adding new contacts, in order to disable the save button if no data has entered I check if the contact object is empty, is there a shorter way to check for an empty complex object?

  let isContactEmpty = true

  Object.keys(contact).forEach(field => {
    if (typeof contact[field] === 'string' && contact[field]) {
      isContactEmpty = false
      return
    }
    if (typeof contact[field] === 'object') {
      contact[field].forEach(fieldVal => {
        if (fieldVal.value.length) {
          isContactEmpty = false
        }
      })
    }
  })
\$\endgroup\$
2
  • \$\begingroup\$ The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How do I ask a good question?. \$\endgroup\$
    – BCdotWEB
    Feb 3, 2021 at 9:28
  • 1
    \$\begingroup\$ Can you please add some possible values for contact variable and what is considered as an empty object? \$\endgroup\$
    – adiga
    Feb 4, 2021 at 12:16

1 Answer 1

2
\$\begingroup\$
  • Is there a particular reason you're using a .forEach() instead of for-of?. Because .forEach() uses a function for iteration, you don't have a way to stop early once you've you know the contact is not empty. If you used for-of instead, you can just return false once you know the result will be false.
  • It looks like you're doing a typeof value === 'object' to check if something is an array. Instead, you can do Array.isArray(value).
  • You're looping over the keys of the object, but you only use the key to get the field's value. Why not just loop over the values instead with Object.values()?

With these suggestions applied, your code would look something like this:

function isContactEmpty(contact) {
  for (const field of Object.values(contact)) {
    if (typeof field === 'string' && field) {
      return false
    } else if (Array.isArray(field)) {
      for (const { value } of field) {
        if (value.length) return false
      }
    }
  }
  return true
}

But, we can take it a step further. Javascript has a higher-level function to do exactly what we're doing with these loops, but without the boilerplate - array.every(). It takes a callback that returns a boolean and gets called with each element of an array. The return value of every() is true if all the callbacks returned true, or false if one of them returned false. (It'll also stop iterating as soon as one callback returns false).

const isContactEmpty = contact => (
  Object.values(contact).every(field => {
    if (typeof field === 'string') return !field
    if (Array.isArray(field)) return areArrayEntriesEmpty(field)
    return true
  })
)

const areArrayEntriesEmpty = field => (
  field.every(({ value }) => value.length === 0)
)
\$\endgroup\$
1
  • \$\begingroup\$ Since your implementation iterate over Object.values(). Maybe field is not a good name for loop variable. \$\endgroup\$
    – tsh
    Feb 7, 2021 at 3:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.