7

I have a users object am trying to use the lodash map() method on it to have it return only the userIds, while filtering out any users with the currentUserId. I wanted to avoid using chain() since it pulls in the entire library, so it seemed that the flow() method is perfect, yet it's not mapping to an array of Id's.

import { map, filter, flow, } from 'lodash'; const users = { 123: { uid: 123 }, 456: { uid: 456 } }; const currentUserId = 123; const userIds = flow( map(user => user.uid), filter(userId => userId !== currentUserId), )(users); 

Unfortunately, this is returning the same object as was passed into it. How can I get an array with the ids of all the users that are not the current user?

9
  • 1
    _.map(user => user.uid) is evaluated before _.flow() is even called. Commented Sep 12, 2018 at 14:27
  • @Pointy This seems like the functional usage of lodash, so shouldn't _.map be the curried invocation that creates a new function that expects an object to execute the mapping against? Unless OP is not using the lodash in its functional form, then the _.map shouldn't executing right after Commented Sep 12, 2018 at 14:32
  • Lodash does not (and cannot) change the way basic JavaScript expression evaluation works. The Lodash _.flow() method expects to be passed an array of function references. That's not what's being done in the posted code. Commented Sep 12, 2018 at 14:33
  • @Pointy I'm not following you. Do you mind providing an answer with the code written correctly? Commented Sep 12, 2018 at 14:34
  • _.map(user => user.uid) is a function call, not a function. Commented Sep 12, 2018 at 14:35

3 Answers 3

9

The answer applies for the standard version of lodash. Please see @Vlaz's answer for a look at the functional programming version of lodash.


When you write _.map(user => user.uid) it is actually invoking the function at that time. What you're really attempting to do is to create function that is similar to _.map, but has one of its arguments already set.

Fortunately, Lodash has a built-in to handle this situation - _.partialRight

const userIds = _.flow( _.partialRight(_.map, user => user.uid) _.partialRight(_.filter, userId => userId !== currentUserId), )(users); 

Documentation


Alternatively if you wish to use plain JS rather than importing a new function, you can simply wrap it in an anonymous function to pass the arguments properly.

const userIds = _.flow( (users) => _.map(users, user => user.uid), (users) => _.filter(users, userId => userId !== currentUserId), )(users); 
Sign up to request clarification or add additional context in comments.

2 Comments

When you write _.map(user => user.uid) it is actually invoking the function at that time. Here is how _.map doesn't invoke the function.
@vlaz If the OP was using the FP version of lodash, I would expect them to have written import { map, filter, flow } from 'lodash/fp';. I was unaware of that variant, but if they were using that library then you are correct. I will update my answer to state that it is for the standard version of lodash
5

It appears that you are not using the functional version of lodash.

var users = { 123: { uid: 123 }, 456: { uid: 456 } }; var currentUserId = 123; const userIds = _.flow( _.map(user => user.uid), _.filter(userId => userId !== currentUserId) )(users); console.log("result with normal lodash", userIds);
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>

var users = { 123: { uid: 123 }, 456: { uid: 456 } }; var currentUserId = 123; const userIds = _.flow( _.map(user => user.uid), _.filter(userId => userId !== currentUserId) )(users); console.log("result with lodash FP", userIds);
<script src="https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)"></script>

As per the Lodash FP guide, you should be importing that into your files

// Load the fp build. var fp = require('lodash/fp'); 

In your case, you can just import just map, filter, and flow but you should get their lodash FP variants.

3 Comments

OMG! I wasn't even aware of the functional version of Lodash! This is so freaking helpful! Thanks!
@Fogmeister it's awesome, I agree. You can manually convert functions from "normal" to FP, if you want/need. You have to pass them through flip/rearg -> curry and I think you might need explicitly set ary for some (most?) otherwise the curry doesn't know the arity with optional parameters. You can flow all these together. It's not ideal, as you need to convert each function you want to use and you might as well use lodash/fp, however it might be of limited use if you are stuck with "normal" lodash and need functional approach in some section.
Currently I was doing all the fp stuff myself with partial and partialRight everywhere. For now the best bit is that I can remove all of those that I was using and just use the fp function instead. :D
3

Can do this with a simple native JS reduce of array created by Object#values() with no more code than using lodash

const users = {123: {uid: 123},456: {uid: 456}}; const currId = 123; const userIds = Object.values(users) .reduce((a, {uid}) => uid !== currId ? a.concat(uid): a, []) console.log(userIds)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.