Alaksandar Jesus Gene
Alaksandar Jesus Gene

Reputation: 6883

Removing object properties with Lodash

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?

Upvotes: 121

Views: 247412

Answers (12)

Anupam Maurya
Anupam Maurya

Reputation: 2201

let asdf = [{"asd": 12, "asdf": 123}, {"asd": 121, "asdf": 1231}, {"asd": 142, "asdf": 1243}]

asdf = _.map(asdf, function (row) { return _.omit(row, ['asd']) })

Upvotes: 0

Danny Sofftie
Danny Sofftie

Reputation: 1051

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)

Upvotes: 0

Webber
Webber

Reputation: 5494

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/

Upvotes: 0

Rameshwari
Rameshwari

Reputation: 1

For array of objects

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

Upvotes: 0

Chris
Chris

Reputation: 58172

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)

Upvotes: 238

yungy
yungy

Reputation: 91

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" }

Upvotes: 9

Mauro Junior
Mauro Junior

Reputation: 91

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

Upvotes: 9

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

Upvotes: 1

iliya.rudberg
iliya.rudberg

Reputation: 789

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

Upvotes: 0

Anupam Maurya
Anupam Maurya

Reputation: 2201

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)

Upvotes: 4

Ori Drori
Ori Drori

Reputation: 191976

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

Upvotes: 81

aercolino
aercolino

Reputation: 2346

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

Upvotes: 0

Related Questions