Skip to content

Conversation

@billzeller
Copy link

Hi,

I've added a function called "omap", which allows functional mapping on objects. It comes in two flavors:

  1. _.omap(o, f)

  2. _.omap(o, keys, f)

  3. calls function f on each key of o, setting o.key to the result of the called function.

  4. calls function f on each key specified by keys, setting o.key to the result of the called function.

The object is modified in place and a copy of the object is returned. To call this function without modifying the called object, .omap(.clone(o), ...) can be used. I made in-place modification the default because this was my most common used case and is cheaper than always copying the object.

This function is useful when storing s set of similar items in an object and wanting to act on all of them in a similar way.

I added tests, but did not add documentation because I had some trouble building the docs (general npm problems).

@jashkenas
Copy link
Owner

Do you have a brief example of how you use omap in your application?

@billzeller
Copy link
Author

Most recently I had a set of nodejs controllers stored in an object. Each controller is a function, and an omap function would allow me to easily annotate the function. For example, I could write:

_(controllers).omap(requireAuthentication)

...to wrap each function in another function which requires authentication. I'm essentially using this wrapper function as an alternative to python style decorators.

(markdown is eating my underscores)

@jashkenas
Copy link
Owner

Mind pasting in the code in question? I find it's always helpful when looking at enhancements like this -- not that I don't see how it could be used, but it's nice to see how it has been used.

@billzeller
Copy link
Author

Sure. Here's a minimal toy example with the guts removed from a simple (M)VC framework I'm working on.

/*
 * This is from a file called comments.js which handles comment
 * modification. For example, 
 * /comments/view -> this.view(...)
 */

this.view = function(req, res) {
  // Get comment id
  // Verify that this.user can read comment (this.user is set by
  //   bz.helpers.requireAuthentication
  // Retrieve comment from DB
  // print comment
};

this.edit = function(req, res) {
  // Get comment id
  // Verify that this.user can edit comment
  // Save edited comment
};

this.remove = function(req, res) {
  // Get comment id
  // Verify that this.user can delete comment
  // Delete comment
};

_(this).omap(['view'], bz.Controller.get);
_(this).omap(['edit', 'remove'], bz.Controller.post);

_(this).omap(bz.helpers.requireAuthentication);

Without this, you'd need something like:

this.remove = bz.helpers.requireAuthentication(bz.Controller.get(function(req, res) {

});

or:

this.remove = bz.helpers.requireAuthentication(bz.Controller.get(this.remove));

for every function.

@billzeller
Copy link
Author

Also, I like the fact that underscore has been kept minimal. If you don't feel like this function is generally useful, that's totally fine.

@jashkenas
Copy link
Owner

I think that your particular use case treads pretty close to things that are already well served by _.wrap and _.compose, which are explicitly built for function wrapping.

I also think that if we added omap, the default would have to be non-destructive (or else you can't call it a "map"), which means that your code would look quite different, because you can't replace this:

_.extend(this, _.omap(this, ['edit', 'remove'], bz.Controller.post));

Which is a bit obfuscated looking, no?

Finally, the method signatures of all currently existing Underscore collection functions return an array of values, and not an object, so this would be a strange outlier, and wouldn't fit in to well with chaining.

So let's close it as a wontfix for the time being, with a note that it's a really nice extension, and one that we should consider adding if we ever add a suite of functions-that-return-object-hashes.

@billzeller
Copy link
Author

Alright, sounds good. Take care.

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants