28

I need to find the fastest way to remove all $meta properties and their values from an object, for example:

{
  "part_one": {
    "name": "My Name",
    "something": "123",
    "$meta": {
      "test": "test123"
    }
  },
  "part_two": [
    {
      "name": "name",
      "dob": "dob",
      "$meta": {
        "something": "else",
        "and": "more"
      }
    },
    {
      "name": "name",
      "dob": "dob"
    }
  ],
  "$meta": {
    "one": 1,
    "two": 2
  }
}

Should become the following given that the $meta property could be at any point in the object so some form of recursion will probably be needed.

{
  "part_one": {
    "name": "My Name",
    "something": "123"
  },
  "part_two": [
    {
      "name": "name",
      "dob": "dob"
    },
    {
      "name": "name",
      "dob": "dob"
    }
  ]
}
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Leon Revill
  • 1,950
  • 3
  • 18
  • 25
  • 3
    Instead of using recursion, make a copy of the object omitting the properties and values you don't want. A general note about recursion in javascript: It depends on the browsers memory allocation which isn't much so recursion is resource heavy and won't work for large data sets unless careful memory management is implemented. – floor Jul 30 '15 at 16:21
  • 1
    The fastest way is to implement it yourself first, then ask for help on making it faster. – Joseph Jul 30 '15 at 16:21
  • You can can traverse an object properties and values with the use of a for in loop or better said for of --> https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of – floor Jul 30 '15 at 16:23

8 Answers8

43

A simple self-calling function can do it.

function removeMeta(obj) {
  for(prop in obj) {
    if (prop === '$meta')
      delete obj[prop];
    else if (typeof obj[prop] === 'object')
      removeMeta(obj[prop]);
  }
}

var myObj = {
  "part_one": {
    "name": "My Name",
    "something": "123",
    "$meta": {
      "test": "test123"
    }
  },
  "part_two": [
    {
      "name": "name",
      "dob": "dob",
      "$meta": {
        "something": "else",
        "and": "more"
      }
    },
    {
      "name": "name",
      "dob": "dob"
    }
  ],
  "$meta": {
    "one": 1,
    "two": 2
  }
}

function removeMeta(obj) {
  for(prop in obj) {
    if (prop === '$meta')
      delete obj[prop];
    else if (typeof obj[prop] === 'object')
      removeMeta(obj[prop]);
  }
}

removeMeta(myObj);

console.log(myObj);
Joseph Marikle
  • 76,418
  • 17
  • 112
  • 129
  • 3
    until JS has tail recursion optimization this is not safe for large objects. It's also unsafe for objects with circular references. – Rene Wooller Sep 13 '16 at 01:39
  • works perfectly, just be sure to clone the object if you need to preserve the original one. – Cattani Simone Sep 11 '19 at 12:53
  • I don't see it working for me. This takes just the first property if that's an object. What about the others? It will not return me the whole object with eliminating that particular prop. Or where am I missing? – Coder Feb 06 '20 at 13:03
40

As @floor commented above:

JSON.parse(JSON.stringify(obj, (k,v) => (k === '$meta')? undefined : v))

Rene Wooller
  • 1,107
  • 13
  • 22
27

// Helper function
function removeProps(obj,keys){
  if(Array.isArray(obj)){
    obj.forEach(function(item){
      removeProps(item,keys)
    });
  }
  else if(typeof obj === 'object' && obj != null){
    Object.getOwnPropertyNames(obj).forEach(function(key){
      if(keys.indexOf(key) !== -1)delete obj[key];
      else removeProps(obj[key],keys);
    });
  }
}
// The object we want to iterate
var obj = {
  "part_one": {
    "name": "My Name",
    "something": "123",
    "$meta": {
      "test": "test123"
    }
  },
  "part_two": [
    {
      "name": "name",
      "dob": "dob",
      "$meta": {
        "something": "else",
        "and": "more"
      }
    },
    {
      "name": "name",
      "dob": "dob"
    }
  ],
  "$meta": {
    "one": 1,
    "two": 2
  }
};
// Utilize the utility
removeProps(obj,['$meta']);
// Show the result
document.body.innerHTML = '<pre>' + JSON.stringify(obj,null,4) + '</pre>';
Kyle
  • 3,935
  • 2
  • 30
  • 44
3

I created this functions when any key is in any level in the object using the reference function of @Joseph Marikle

const _ = require("lodash");
const isObject = obj => obj != null && obj.constructor.name === "Object";
const removeAttrDeep = (obj, key) => {
    for (prop in obj) {
      if (prop === key) delete obj[prop];
      else if (_.isArray(obj[prop])) {
        obj[prop] = obj[prop].filter(k => {
          return !_.isEmpty(removeAttrDeep(k, key));
        });
     } else if (isObject(obj[prop])) removeAttrDeep(obj[prop], key);
    }
    return obj;
 };

EXAMPLE:

 const _obj = {
       a: "b", b: "e", c: { a: "a", b: "b", c: "c"},
       d: [ { a: "3" }, { b: ["2", "3"] }]};
 console.log(removeAttrDeep(_obj, "b"));
Alex Montoya
  • 4,697
  • 1
  • 30
  • 31
2

(Apologies, I do not yet have enough reputation points to comment directly.)

Just FYI, typeof null === 'object', so in the removeMeta() example offered by @joseph-marikle, the function will recurse on a null value.

Read more here: why is typeof null "object"?

dpmott
  • 282
  • 3
  • 8
1

Here is a function that takes either a string or an array of strings to remove recursively (based on Joseph's answer):

// removes all propsToRemove (passed as an array or string), drilling down up to maxLevel times
// will modify the input and return it
du.removeAllPropsFromObj = function(obj, propsToRemove, maxLevel) {
    if (typeof maxLevel !== "number") maxLevel = 10
    for (var prop in obj) {
        if (typeof propsToRemove === "string" && prop === propsToRemove)
            delete obj[prop];
        else if (propsToRemove.indexOf(prop) >= 0)      // it must be an array
            delete obj[prop];
        else if (typeof obj[prop] === "object" && maxLevel>0)
            du.removeAllPropsFromObj(obj[prop], propsToRemove, maxLevel-1);
    }
    return obj
}
Nico
  • 4,248
  • 1
  • 20
  • 19
1
// recursively delete a key from anywhere in the object
// will mutate the obj - no need to return it
const deletePropFromObj = (obj, deleteThisKey) => {
  if (Array.isArray(obj)) {
    obj.forEach(element => deletePropFromObj(element, deleteThisKey))
  } else if (typeof obj === 'object') {
    for (const key in obj) {
      const value = obj[key]
      if (key === deleteThisKey) delete obj[key]
      else deletePropFromObj(value, deleteThisKey)
    }
  }
}

deletePropFromObj(obj, '$meta');
Jonathan
  • 167
  • 1
  • 8
1

This should avoid any issues with circular references

const recursiveRedact = <T extends Object>(obj: T, keys: Set<string>, visitedIn?: Set<any>): T => {
  if (obj == null || typeof obj !== 'object') return obj;
  const visited = visitedIn ?? new Set();
  visited.add(obj);
  Object.entries(obj).forEach(([key, val]) => {
    if (keys.has(key)) {
      obj[key as keyof T] = '[redacted]' as any;
    } else if (typeof val === 'object' && !visited.has(val)) {
      recursiveRedact(val, keys, visited);
    }
  });
  return obj;
};
mowwwalker
  • 16,634
  • 25
  • 104
  • 157
  • 1
    Thanks a lot. That is exactly what I was looking for. I wanted to write an object to a json file and it failed due to circular dependencies. Using your function fixed my issue. – Mina Luke Jan 02 '23 at 05:08
  • 1
    @MinaLuke glad I was able to help! Good thing you commented too. The code above ended up needing a fix to account for `typeof null == "object"` so I'll update the answer now – mowwwalker Jan 03 '23 at 06:24