0

Is there a Lodash or Underscore method which can find if an object has only the given keys of that object. I would like a Lodash or Underscore implementation even though this sounds trivial using native JS.

For example if my object looks like and assuming there is a lodash method named hasOnly

const obj = {
    name: undefined,
  age: 15,
  school: 'Some school'
}

_.hasOnly(obj,['name','age']) //return false

_.hasOnly(obj,['name','age','city']) //return false

_.hasOnly(obj,['name','age','school']) //return true

I couldn't seem to find a way in the docs

7
  • 3
    Side question, if it "sounds trivial using native JS" as you say yourself, why require a library for it? Commented Jan 31, 2022 at 11:42
  • Do you want a native JS answer if one cannot be found in lodash/underscore docs? Commented Jan 31, 2022 at 11:43
  • @JeremyThille my project requires it since they are using it exclusively everywhere Commented Jan 31, 2022 at 11:43
  • So if your project required you to write a webserver, would you look for a method in lodash for that? Or, to put it another way if your woodworking project required you to bang in a nail, but you only had a wallpaper stripper, would you use that or look for a hammer? Commented Jan 31, 2022 at 11:45
  • It doesn't make sense to force use lodash/underscore when it can be easily done using native JS Commented Jan 31, 2022 at 11:45

3 Answers 3

4

Quick and dirty:

hasOnly = (obj, props) => _.isEqual(_.keys(obj).sort(), props.sort())

The sorting is done because we are comparing arrays.

As an alternative, one could turn both props and _.keys(obj) into objects where the props and _.keys(obj) are the keys, whereas the value is a dummy one, always the same, such as 1. The function to do so could be something like this:

make1ValuedObj = keys => _.zipObject(keys, Array(keys.length).fill(1))

Then one would pass those to _.isEqual without having to sort anything:

hasOnly = (obj, props) => _.isEqual(make1ValuedObj(_.keys(obj)), make1ValuedObj(props))

The reality is that a kind of "sorting" has to happen when you construct the objects, so I don't think there's a real advantage over the solution above.

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

5 Comments

I think that should be _.zipObject not _.zipObj - but its getting slower
@Jamiec, you're right. I picked a shorthand from lodash/fp.
i wonder why they dont have an implementation for this. i thought there was only i couldn't find it. anyway thanks for the answers
@Jamiec, yes, it is slower. And it was still slow in the first place. I wander why. I'll probably ask a question myself for this specific use case (I've litterally rewritten your solution using lodash's equivalent of each of the memebr functions you used, and it's slow nonetheless; disappointing).
@Jamiec, fwiw, fyi.
2

The native solution will be faster in almost all cases:

const obj = {
  name: undefined,
  age: 15,
  school: 'Some school'
}

const hasOnly = (obj,props) => {
    var objProps = Object.keys(obj)
    return objProps.length == props.length && props.every(p => objProps.includes(p))
}

console.log(hasOnly(obj,['name','age'])) //return false

console.log(hasOnly(obj,['name','age','city'])) //return false

console.log(hasOnly(obj,['name','age','school'])) //return true

Benchmarking this against the other answer using lodash shows the lodash solution to be 95% slower (on my machine)

Benchmarks: https://jsbench.me/r9kz2mwr9c/1

Comments

1

I think Enlico's answer is fine, but for completeness I'll mention another option which doesn't require sorting. This is based on comparing objects directy instead of comparing arrays of keys.

Note that the code below assumes the original Underscore. For Lodash, replace _.mapObject by _.mapValues.

// replace all properties by true to prevent costly recursion
const mask = obj => _.mapObject(obj, _.constant(true));

function hasOnly(obj, keys) {
    const masked = mask(obj);
    // compare obj to a trimmed version of itself
    return _.isEqual(masked, _.pick(masked, keys));
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.