Home > other >  How to check if an object has other than specific properties
How to check if an object has other than specific properties

Time:08-07

I have an object obj which has n number of possible properties

lets say some of them are known,

const someKnownProps = ["props.abc", "xyz"]; // or more

I want to know if obj has other than known properties in it.

To clarify:

obj can look like this:

obj = {
   props: {
     abc: {
       def: 1
     },
     ghi: {
       jkl: 2
     }
   },
   xyz: 3
}

Doing Object.keys only return first level children, in this case it will return props not props.abc

CodePudding user response:

You can use Object.keys to get all keys and filter the keys which aren't included in the someKnownProps array.

const obj = {
"props.abc": 1,
"xyz": 2,
"three": 3,
"four": 4,
}

const someKnownProps = ["props.abc", "xyz"]; // or more

const unknownKeys = Object.keys(obj).filter(key => !someKnownProps.includes(key))

console.log(unknownKeys)

CodePudding user response:

Similar to what Mina said:

let obj = {one: 1, two: 2, three: 3};
let knownKeys = ['one', 'two'];

for (let key in obj) {
  if (!knownKeys.includes(key)) {
    console.log(key);
  }
}

CodePudding user response:

There are two (unrelated) tasks involved in this question:

  1. Traversal of an object's properties
  2. Comparison of a set of traversed object properties to a list of strings representing dot-notation-formatted object property accessors

While I'm sure the former has been previously discussed on SO, I'll provide an implementation of such an algorithm below in order to address the details of this question.

This is essentially a specific case of recursion where each cycle starts with these inputs:

  • an object
  • a dot-notation-formatted path
  • a Set of existing such paths

The code below includes inline comments explaining what's happening, and there are some console.log statements at the end to help you visualize some example results based on the data in your question. If something is unclear after reviewing the code, feel free to leave a comment.

'use strict';

/** @returns whether value is a non-null, non-array object */
function isObject (value) {
  return value !== null && typeof value === 'object' && !Array.isArray(value);
}

/** @returns the enumerable (optionally including inherited) keys of an object */
function getKeys (obj, includeInherited = false) {
  if (!includeInherited) return Object.keys(obj);
  const keys = new Set();
  let o = obj;
  while (o !== null) {
    for (const key of Object.keys(o)) keys.add(key);
    o = Object.getPrototypeOf(o);
  }
  return [...keys];
}

/**
 * @returns an array of strings representing all traversible branches
 * of child objects, each formatted as a combined path of dot-notation
 * property accessors
 */
function findObjectPaths (
  obj,
  {
    includeInherited = false,
    currentPath = '',
    paths = new Set(),
    skipReturn = false,
  } = {},
) {
  for (const key of getKeys(obj, includeInherited)) {
    // Append the current dot-notation property accessor
    // to the existing path of this object:
    const path = `${currentPath}.${key}`;
    // Add it to the set:
    paths.add(path);
    const o = obj[key];
    // Recurse if the child value is an object:
    if (isObject(o)) {
      findObjectPaths(o, {
        includeInherited,
        currentPath: path,
        paths,
        skipReturn: true,
      });
    }
  }

  // If this is not a sub-cycle (it's the top-level invocation), then convert
  // the set to an array and remove the first "." from each string
  if (!skipReturn) return [...paths].map(p => p.slice(1));
}


// Use:

const obj = {
  props: {
    abc: {
      def: 1,
    },
    ghi: {
      jkl: 2,
    },
  },
  xyz: 3,
};

let someKnownProps = ['props.abc', 'xyz'];
let objectPaths = findObjectPaths(obj);
let hasOtherProps = objectPaths.some(path => !someKnownProps.includes(path));
console.log(hasOtherProps); // true

// An example of all of the paths in the object above:
someKnownProps = [
  'props',
  'props.abc',
  'props.abc.def',
  'props.ghi',
  'props.ghi.jkl',
  'xyz',
];

objectPaths = findObjectPaths(obj);
hasOtherProps = objectPaths.some(path => !someKnownProps.includes(path));
console.log(hasOtherProps); // false


// Finally, comparing the results of inherited vs non-inherited enumeration:

const objWithoutOwnProps = Object.create({
  props: {
    abc: {
      def: 1,
    },
    ghi: {
      jkl: 2,
    },
  },
  xyz: 3,
});

console.log(
  'Non-inherited props:',
  findObjectPaths(objWithoutOwnProps),
);

console.log(
  'Inherited props:',
  findObjectPaths(objWithoutOwnProps, {includeInherited: true}),
);

  • Related