0

I would like to count how much filled fields i have in a nested object

example:

const data = {
  name: 'Alex',
  lastName: '',
  age: 24,
  lang: { lang1: 'fr', lang2: 'en' },
  courses: { c1: '', c2: 'math', c3: '' },
  books: { book1: '', book2: 'book2' },
};

result should be : 6 (Alex, 24, fr, en, math, book2)

5 Answers5

2

You could reduce the values from the object with a recursive approach.

const
    count = object => Object
        .values(object)
        .reduce((s, v) => s + (v && typeof v === 'object'
            ? count(v)
            : +!!v
        ), 0),
    data = { name: 'Alex', lastName: '', age: 24, lang: { lang1: 'fr', lang2: 'en' }, courses: { c1: '', c2: 'math', c3: '' }, books: { book1: '', book2: 'book2' } };
    
console.log(count(data));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

I have borrowed travese from another SO post

function traverse(o,func) {
    for (var i in o) {
        func.apply(this,[i,o[i]]);  
        if (o[i] !== null && typeof(o[i])=="object") {
            //going one step down in the object tree!!
            traverse(o[i],func);
        }
    }
}

So now we can do this:

const data = {
    name: 'Alex',
    lastName: '',
    age: 24,
    lang: { lang1: 'fr', lang2: 'en' },
    courses: { c1: '', c2: 'math', c3: '' },
    books: { book1: '', book2: 'book2' },
};

function getCount(obj) {
    // Determine if a value is 'filled'
    let isFilled = (value) => value !== '' && typeof value !== 'object'

    let count = 0
    traverse(data, (key, value) => {
        if (isFilled(value))
            count++
    })
    return count
}

let result = getCount(data)
Mal
  • 355
  • 2
  • 8
0

As already stated in a comment to your question, it would better manners to show your progress before you got stuck. :) Here's my take on it.

const data = {
  name: 'Alex',
  lastName: '',
  age: 24,
  lang: { lang1: 'fr', lang2: 'en' },
  courses: { c1: '', c2: 'math', c3: '' },
  books: { book1: '', book2: 'book2' },
};

const countElements = (obj) => {
  // variable for counting element
  let validValueCount = 0;

  // get values for object
  const values = Object.values(obj);

  // filter out falsy values
  const validValues = values.filter((el) => !!el);

  // iterate through all valid values, count non-objects and recursively call this function for objects
  validValues.map((el) => {
    validValueCount =
      validValueCount + (Object.prototype.toString.call(el) === "[object Object]" ? countElements(el) : 1);
  });

  // return sum of all counted values
  return validValueCount;
};
const ans = countElements(data);
console.log(ans);
charmful0x
  • 155
  • 9
shte
  • 121
  • 1
  • 4
  • Thanks @charmful0x for improving answer (using Object.prototype.toString.call instead of typof). Example for array let's us understand why it's so Object.prototype.toString.call([]) -> "[object Array]" – shte May 04 '21 at 10:37
0

const data = {
  name: 'Alex',
  lastName: '',
  age: 24,
  lang: { lang1: 'fr', lang2: 'en' },
  courses: { c1: '', c2: 'math', c3: '' },
  books: { book1: '', book2: 'book2' },
}

const isObject = o => typeof o === 'object' && o !== null
const calcFilled = o => Object.entries(o)
    .reduce((acc, [k, v]) => {
        if (isObject(v)) acc += calcFilled(v)
        else if (v) acc++
        return acc
    }, 0)
console.log(calcFilled(data))
ulou
  • 5,542
  • 5
  • 37
  • 47
0

For nested objects only.

const data = {
  name: 'Alex',
  lastName: '',
  age: 24,
  lang: { lang1: 'fr', lang2: 'en' },
  courses: { c1: '', c2: 'math', c3: '' },
  books: { book1: '', book2: 'book2' },
};

let count = 0;

const checkEmptyField = (obj) => {
  Object.values(obj).forEach((value) => {
    if (typeof value !== 'object' && value !== '') count++;
    if (typeof value === 'object') {
      checkEmptyField(value);
    }
  });
};

checkEmptyField(data);

console.log(count); // logs 6
Aseer KT
  • 473
  • 5
  • 8