-1

I want to check if an object already exists in a given object by only having the object. For instance:

const information = {
    ...
    city: {
        Streetname: ''
    }
}

Now, I get the city object and want to check if it is already in the information object (without knowing the property name). The city could be n deep in the information object.

Fabian
  • 119
  • 3
  • 9
  • So you want to check if `information` has a property called `city`? – Jack Bashford Feb 10 '19 at 01:01
  • No, my input is the city object and I want to check if it is already in the information object. So my goal is actually getting the property name – Fabian Feb 10 '19 at 01:02
  • So, you want to see if `information` contains the property `{ Streetname: '' }`? – Jack Bashford Feb 10 '19 at 01:03
  • There could also be `city2` with the `Streetname` property... All I have is `Object {Streetname: ''}` – Fabian Feb 10 '19 at 01:05
  • Okay, so you want to check if `{ Streetname: '' }` is inside the `information` object? – Jack Bashford Feb 10 '19 at 01:06
  • Sorry for explaining to badly... I want to check if my input object matches an object in `information` and then get the property name | path. But I couldnt find a way of comparing objects so far – Fabian Feb 10 '19 at 01:08
  • Please provide a [mcve] and edit the question with more specific details – charlietfl Feb 10 '19 at 01:16

2 Answers2

0

const contains = (item, data) => item === data || Object.getOwnPropertyNames(data).some(prop => contains(item, data[prop]));

const information = {
    city: {
        Streetname: ''
    }
}

console.log(contains(information.city, information));

console.log(contains({}, information));
Adrian Brand
  • 20,384
  • 4
  • 39
  • 60
0

To get the property name of an object you can use Object.keys(). The first problem solved.
Now we need to iterate through the whole object including nested objects. This is the second problem.
And compare it to a query object. This is the third problem.

I assume that we have an object that only contains "simple" though nested objects with primitive values (I do not consider objects with functions or arrays)

// let's assume we have this object
const information = {
    city: {
        Streetname: 'streetname1'
    },
    house: {
        color: "blue",
        height: 100,
        city: {
            findMe: { Streetname: '' } // we want to get the path to this property 'findMe'
        }
    },
    findMeToo: {
        Streetname: '' // we also want to get the path to this proeprty 'findMeToo'
    },
    willNotFindMe: {
        streetname: '' // case sensetive
    }  
}

// this is our object we want to use to find the property name with
const queryObject = {
        Streetname : ''
}

If you use === to compare Objects you will always compare by reference. In our case, we are interested to compare the values. There is a rather extensive checking involved if you want to do it for more complex objects (read this SO comment for details), we will use a simplistic version:

// Note that this only evaluates to true if EVERYTHING is equal.
// This includes the order of the properties, since we are eventually comparing strings here.
JSON.stringify(obj1) === JSON.stringify(obj2) 

Before we start to implement our property pathfinder I will introduce a simple function to check if a given value is an Object or a primitive value.

function isObject(obj) {
  return obj === Object(obj); // if you pass a string it will create an object and compare it to a string and thus result to false
}

We use this function to know when to stop diving deeper since we reached a primitive value which does not contain any further objects. We loop through the whole object and dive deeper every time we find a nested object.

function findPropertyPath(obj, currentPropertyPath) {
    const keys = isObject(obj) ? Object.keys(obj) : []; // if it is not an Object we want to assign an empty array or Object.keys() will implicitly cast a String to an array object  
    const previousPath = currentPropertyPath; // set to the parent node

    keys.forEach(key => {
        const currentObj = obj[key];
        currentPropertyPath = `${previousPath}.${key}`;

        if (JSON.stringify(currentObj) === JSON.stringify(queryObject)) console.log(currentPropertyPath); // this is what we are looking for
        findPropertyPath(currentObj, currentPropertyPath); // since we are using recursion this is not suited for deeply nested objects
    })
}

findPropertyPath(information, "information"); // call the function with the root key

This will find all "property paths" that contain an object that is equal to your query object (compared by value) using recursion.

information.house.city.findMe
information.findMeToo
  • Cool, that is what I was looking for. Didn't knew that I can compare obj with casting them to JSON strings. However this is a costly operation. Thank you Alberti – Fabian Feb 10 '19 at 09:52
  • Comparing objects of arbitrary content/depth is always somehow a costly operation. Also since I do not expect that your objects are big enough that you will experience any performance issues since you only stringify parts of the objects. The recursion might be a bigger problem. – Alberti Buonarroti Feb 10 '19 at 10:00
  • JSON.stringify({ prop1: 1, prop2: 2 }) !== JSON.stringify({ prop2: 2, prop1: 1 }), comparing via stringify is not going to give a reliable result. – Adrian Brand Feb 10 '19 at 10:03
  • Yes, read my comment in my code: // Note that this only evaluates to true if EVERYTHING is equal. // This includes the order of the properties, since we are eventually comparing strings here. – Alberti Buonarroti Feb 10 '19 at 11:20