Removing object properties with Lodash

JavascriptLodash

Javascript Problem Overview


I have to remove unwanted object properties that do not match my model. How can I achieve it with Lodash?

My model is:

var model = {
   fname: null,
   lname: null
}

My controller output before sending to the server will be:

var credentials = {
    fname: "xyz",
    lname: "abc",
    age: 23
}

I am aware I can use

delete credentials.age

but what if I have lots of unwanted properties? Can I achieve it with Lodash?

Javascript Solutions


Solution 1 - Javascript

You can approach it from either an "allow list" or a "block list" way:

// Block list
// Remove the values you don't want
var result = _.omit(credentials, ['age']);

// Allow list
// Only allow certain values
var result = _.pick(credentials, ['fname', 'lname']);

If it's reusable business logic, you can partial it out as well:

// Partial out a "block list" version
var clean = _.partial(_.omit, _, ['age']);

// and later
var result = clean(credentials);

Note that Lodash 5 will drop support for omit

A similar approach can be achieved without Lodash:

const transform = (obj, predicate) => {
    return Object.keys(obj).reduce((memo, key) => {
    if(predicate(obj[key], key)) {
        memo[key] = obj[key]
    }
    return memo
    }, {})
}

const omit = (obj, items) => transform(obj, (value, key) => !items.includes(key))

const pick = (obj, items) => transform(obj, (value, key) => items.includes(key))

// Partials
// Lazy clean
const cleanL = (obj) => omit(obj, ['age'])

// Guarded clean
const cleanG = (obj) => pick(obj, ['fname', 'lname'])


// "App"
const credentials = {
    fname:"xyz",
    lname:"abc",
    age:23
}

const omitted = omit(credentials, ['age'])
const picked = pick(credentials, ['age'])
const cleanedL = cleanL(credentials)
const cleanedG = cleanG(credentials)

Solution 2 - Javascript

Get a list of properties from model using _.keys(), and use _.pick() to extract the properties from credentials to a new object:

var model = {
   fname:null,
   lname:null
};

var credentials = {
    fname:"xyz",
    lname:"abc",
    age:23
};

var result = _.pick(credentials, _.keys(model));

console.log(result);

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>

If you don't want to use Lodash, you can use Object.keys(), and Array.prototype.reduce():

var model = {
   fname:null,
   lname:null
};

var credentials = {
    fname:"xyz",
    lname:"abc",
    age:23
};

var result = Object.keys(model).reduce(function(obj, key) {
  obj[key] = credentials[key];
  return obj;
}, {});

console.log(result);

Solution 3 - Javascript

You can easily do this using _.pick:

var model = {
  fname: null,
  lname: null
};

var credentials = {
  fname: 'abc',
  lname: 'xyz',
  age: 2
};

var result = _.pick(credentials, _.keys(model));


console.log('result =', result);

<script src="https://cdn.jsdelivr.net/lodash/4.16.4/lodash.min.js"></script>

But you can simply use pure JavaScript (specially if you use ECMAScript 6), like this:

const model = {
  fname: null,
  lname: null
};

const credentials = {
  fname: 'abc',
  lname: 'xyz',
  age: 2
};

const newModel = {};

Object.keys(model).forEach(key => newModel[key] = credentials[key]);

console.log('newModel =', newModel);

Solution 4 - Javascript

Lodash unset is suitable for removing a few unwanted keys.

const myObj = {
    keyOne: "hello",
    keyTwo: "world"
}

unset(myObj, "keyTwo");

console.log(myObj); /// myObj = { keyOne: "hello" }

Solution 5 - Javascript

Here I have used omit() for the respective 'key' which you want to remove... by using the Lodash library:

var credentials = [{
        fname: "xyz",
        lname: "abc",
        age: 23
}]

let result = _.map(credentials, object => {
                       return _.omit(object, ['fname', 'lname'])
                   })

console.log('result', result)

Solution 6 - Javascript

You can use _.omit() for emitting the key from a JSON array if you have fewer objects:

_.forEach(data, (d) => {
    _.omit(d, ['keyToEmit1', 'keyToEmit2'])
});

If you have more objects, you can use the reverse of it which is _.pick():

_.forEach(data, (d) => {
    _.pick(d, ['keyToPick1', 'keyToPick2'])
});

Solution 7 - Javascript

To select (or remove) object properties that satisfy a given condition deeply, you can use something like this:

function pickByDeep(object, condition, arraysToo=false) {
  return _.transform(object, (acc, val, key) => {
    if (_.isPlainObject(val) || arraysToo && _.isArray(val)) {
      acc[key] = pickByDeep(val, condition, arraysToo);
    } else if (condition(val, key, object)) {
      acc[key] = val;
    }
  });
}

https://codepen.io/aercolino/pen/MWgjyjm

Solution 8 - Javascript

This is my solution to deep remove empty properties with Lodash:

const compactDeep = obj => {
    const emptyFields = [];

    function calculateEmpty(prefix, source) {
        _.each(source, (val, key) => {
           if (_.isObject(val) && !_.isEmpty(val)) {
                calculateEmpty(`${prefix}${key}.`, val);
            } else if ((!_.isBoolean(val) && !_.isNumber(val) && !val) || (_.isObject(val) && _.isEmpty(val))) {
                emptyFields.push(`${prefix}${key}`);
            }
        });
    }

    calculateEmpty('', obj);

    return _.omit(obj, emptyFields);
};

Solution 9 - Javascript

For array of objects

model = _.filter(model, a => {
          if (!a.age) { return a }
        })

Solution 10 - Javascript

Recursively removing paths.

I just needed something similar, not removing just keys, but keys by with paths recursively.

Thought I'd share.

Simple readable example, no dependencies

/**
 * Removes path from an object recursively.
 * A full path to the key is not required.
 * The original object is not modified.
 *
 * Example:
 *   const original = { a: { b: { c: 'value' } }, c: 'value'  }
 *
 *   omitPathRecursively(original, 'a') // outputs: { c: 'value' }
 *   omitPathRecursively(original, 'c') // outputs: { a: { b: {} } }
 *   omitPathRecursively(original, 'b.c') // { a: { b: {} }, c: 'value' }
 */
export const omitPathRecursively = (original, path, depth = 1) => {
  const segments = path.split('.')
  const final = depth === segments.length

  return JSON.parse(
    JSON.stringify(original, (key, value) => {
      const match = key === segments[depth - 1]

      if (!match) return value
      if (!final) return omitPathRecursively(value, path, depth + 1)
      return undefined
    })
  )
}

Working example: https://jsfiddle.net/webbertakken/60thvguc/1/

Solution 11 - Javascript

While looking for a solution that would work for both arrays and objects, I didn't find one and so I created it.

/**
 * Recursively ignore keys from array or object
 */
const ignoreKeysRecursively = (obj, keys = []) => {
  const keyIsToIgnore = (key) => {
    return keys.map((a) => a.toLowerCase()).includes(key)
  }

  const serializeObject = (item) => {
    return Object.fromEntries(
      Object.entries(item)
        .filter(([key, value]) => key && value)
        .reduce((prev, curr, currIndex) => {
          if (!keyIsToIgnore(curr[0]))
            prev[currIndex] =
              [
                curr[0],
                // serialize array
                Array.isArray(curr[1])
                  ? // eslint-disable-next-line
                  serializeArray(curr[1])
                  : // serialize object
                  !Array.isArray(curr[1]) && typeof curr[1] === 'object'
                  ? serializeObject(curr[1])
                  : curr[1],
              ] || []
          return prev
        }, []),
    )
  }

  const serializeArray = (item) => {
    const serialized = []
    for (const entry of item) {
      if (typeof entry === 'string') serialized.push(entry)
      if (typeof entry === 'object' && !Array.isArray(entry)) serialized.push(serializeObject(entry))
      if (Array.isArray(entry)) serialized.push(serializeArray(entry))
    }
    return serialized
  }

  if (Array.isArray(obj)) return serializeArray(obj)
  return serializeObject(obj)
}

// usage
const refObject = [{name: "Jessica", password: "ygd6g46"}]
// ignore password
const obj = ignoreKeysRecursively(refObject, ["password"])
// expects returned array to only have name attribute
console.log(obj)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlaksandar Jesus GeneView Question on Stackoverflow
Solution 1 - JavascriptChrisView Answer on Stackoverflow
Solution 2 - JavascriptOri DroriView Answer on Stackoverflow
Solution 3 - JavascriptMauro JuniorView Answer on Stackoverflow
Solution 4 - JavascriptyungyView Answer on Stackoverflow
Solution 5 - JavascriptAnupam MauryaView Answer on Stackoverflow
Solution 6 - JavascriptPhaneendra Charyulu KanduriView Answer on Stackoverflow
Solution 7 - JavascriptaercolinoView Answer on Stackoverflow
Solution 8 - Javascriptiliya.rudbergView Answer on Stackoverflow
Solution 9 - JavascriptRameshwari View Answer on Stackoverflow
Solution 10 - JavascriptWebberView Answer on Stackoverflow
Solution 11 - JavascriptDanny SofftieView Answer on Stackoverflow