0

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

5
  • 2
    What is the wanted result? Just "yes" or "no", or a list of those other properties? How about the properties in the prototype(chain)? Commented Aug 7, 2022 at 13:37
  • You may want to take a look at this link as well. Commented Aug 7, 2022 at 13:51
  • The task is to know if obj has other than known properties, if it has other property/properties result should be yes In my case I can ignore prototype chain, a solution explaining inherited properties will be appreciated! Commented Aug 7, 2022 at 13:54
  • Even Object.getOwnPropertyNames returns props not props.abc Commented Aug 7, 2022 at 14:06
  • Ignoring what constitutes a valid object property accessor for a moment (although it is critical to the details of this problem): In your example data, you show props.abc in the "known props", but don't include props. If an object has a property at obj.props.abc, then it will certainly have obj.props, so — given the details of your case — the result would always be "the object has more properties than provided". Commented Aug 7, 2022 at 14:36

3 Answers 3

2

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)

Sign up to request clarification or add additional context in comments.

2 Comments

If you need a bool, you can use some: Object.keys(obj).some(key => !someKnownProps.includes(key))
props here can also be an object inside obj doing Object.keys will return "props" not its nested children, im updating my question to clarify this case
1

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}),
);

Comments

0

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);
  }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.