Tapan Dave
Tapan Dave

Reputation: 273

finding undefined or null value from object inside object

i would like to push keys inside array if found undefined or null

const obj = {
  name:'ab',
  edu:'av',
  degres:{
    a1:'',
    b1:'1'
  },
  platform:undefined
 }

i want an output like

  `['a1','platform']`

as the value for a1 and platform were null and undefined

i have treid this solution but it doesnt work

 function iterater(obj){
  let blankValues = [];
  Object.keys(obj).map((key) => {
      if (obj.hasOwnProperty(key) && (typeof obj[key] === "object")) {
        iterater(obj[key])
      } else {
          if (typeof obj[key] === "undefined" || obj[key] === ''){
            blankValues.push(key);
         }
      }
    })
  return blankValues;

}

but this somehow only return ['platform'] only,but the expected output should be ['platform','a1'],i think when running iterater(obj[key]),the value of array (blankValues) gets blank as it doesnt perserve it,but please help me with appropriate logic and structure

Upvotes: 2

Views: 1032

Answers (5)

Rory McCrossan
Rory McCrossan

Reputation: 337570

The issue is because you're re-defining blankValues as an empty array in every iteration of the recursive loop. To fix this you could accept the array as an optional argument of the function so that values get pushed to it on each iteration.

Also note that, as @ziggy wiggy pointed out in the comments, your logic will fail when a null value is encountered as typeof obj[key] === "object" would be true. You need a specific null check too.

const obj = {
  name: 'ab',
  edu: 'av',
  degres: {
    a1: '',
    b1: '1'
  },
  platform: undefined,
  foo: null
}

function iterater(obj, arr) {
  arr = arr || [];
  Object.keys(obj).map((key) => {
    if (obj.hasOwnProperty(key) && (typeof obj[key] === "object") && obj[key] !== null) {
      iterater(obj[key], arr)
    } else {
      if (typeof obj[key] === "undefined" || obj[key] === null || obj[key].trim() === '') {
        arr.push(key);
      }
    }
  })
  return arr;
}

console.log(iterater(obj));

Note that I also added a trim() call to test the empty string. Your previous logic would accept whitespace-filled strings as valid values.

Upvotes: 4

Nina Scholz
Nina Scholz

Reputation: 386610

You could take a check for falsy keys and return the key, if the property is an object, the check the object.

const
    getFalsy = o => Object.keys(o).reduce((r, k) => {
        if (!o[k]) return [...r, k];
        if (typeof o[k] === 'object') return [...r, ...getFalsy(o[k])];
        return r;
    }, []),
    object = { name: 'ab', edu: 'av', degres: { a1: '', b1: '1' }, platform: undefined };

console.log(getFalsy(object));

Upvotes: 0

jo_va
jo_va

Reputation: 13963

You must push the result returned by the recursive call to your array. Change:

iterater(obj[key])

for:

blankValues.push(...iterater(obj[key]))

const obj = {
  name: 'ab',
  edu: 'av',
  degres: {
    a1: '',
    b1: '1'
  },
  platform: undefined
}

 function iterater(obj){
  let blankValues = [];
  Object.keys(obj).map((key) => {
      if (obj.hasOwnProperty(key) && (typeof obj[key] === "object")) {
        blankValues.push(...iterater(obj[key]))
      } else {
          if (typeof obj[key] === "undefined" || obj[key] === ''){
            blankValues.push(key);
         }
      }
    })
  return blankValues;

}

console.log(iterater(obj));

Here is another way to do it using Object.entries(), Object.keys(), Array.reduce(), Array.flat() and Array.isArray(). This implementation works for arrays too.

const obj = {
  name:'ab',
  edu:'av',
  something: [{ a: 1 }, { a: '' }],
  degres:{
    a1:'',
    b1:'1'
  },
  platform:undefined
};

function getEmptyProps(obj) {
  if (!Object.keys(obj).length) { return []; }
  return Object.entries(obj).reduce((acc, [key, val]) => {
    if (val === undefined || val === null || val.toString().trim() === '') {
      acc.push(key);
    } else if (Array.isArray(val)) {
      acc.push(val.map(getEmptyProps).flat());
    } else if (typeof val === 'object') {
      acc.push(getEmptyProps(val));
    }
    return acc.flat();
  }, []);
}

console.log(getEmptyProps(obj))

Upvotes: 0

Yury Tarabanko
Yury Tarabanko

Reputation: 45121

You need to consume the result of recursive call. For example add it back to blankValues like this blankValues.push(...iterater(obj[key]))

const obj = {
  name:'ab',
  edu:'av',
  degres:{
    a1:'',
    b1:'1'
  },
  platform:undefined
 }

 function iterater(obj){
  let blankValues = [];
  Object.keys(obj).map((key) => {
      if (obj.hasOwnProperty(key) && (typeof obj[key] === "object")) {
        blankValues.push(...iterater(obj[key]))
      } else {
          if (typeof obj[key] === "undefined" || obj[key] === ''){
            blankValues.push(key);
         }
      }
    })
  return blankValues;

}

console.log(iterater(obj))

Upvotes: 0

Nenri
Nenri

Reputation: 528

As you said yourself, when you call iterater(obj[key]) it sets a new local blankValues and puts values in it. So i think you should put blankValues outside the function. And then you don't have to return it (or you can if you want it as a return value).

Or you can pass blankValues as a parameter of iterater in both the main call and the "inside" call

Upvotes: 0

Related Questions