rtfeldman / seamless-immutable

Immutable data structures for JavaScript which are backwards-compatible with normal JS Arrays and Objects.
BSD 3-Clause "New" or "Revised" License
5.37k stars 195 forks source link

Modify `.without` to take a predicate determining whether to exclude a property #87

Closed holyjak closed 8 years ago

holyjak commented 8 years ago

It would be awesome to be able to use .without in the same way as lodash' omit, i.e. like this:

Immutable({a:1, b:2, c:3}).without((value, key) => return key === "b" || value === 3)
=> {a: 1}

in addition to passing in the property name(s).

I can try to implement if if it is an acceptable feature.

rtfeldman commented 8 years ago

Is there a common use case this makes nicer? I like to keep the library as simple as possible, so my default is not to add new functionality unless there's a strong motivating case for it. :smiley:

holyjak commented 8 years ago

I understand the desire to keep the library small and appreciate it. I am not sure how common my use case is. In each case, accepting a function instead of values provides the ultimate power to the user. My particular use case was to filter out properties, that did not satisfy a condition:

const unknownProducts = _.chain(products).keys().reject(knownProduct).value();
const onlyKnownProducts = products.without(unknownProducts);
// I would have preferred:
const onlyKnownProducts = products.without((product, productId) => ! knownProduct(productId));

I do not think that it would either be complex or require many lines of code to add this functionality:

function without(keysToRemove) {
    // Calling .without() with no arguments is a no-op. Don't bother cloning.
    if (arguments.length === 0) {
      return this;
    }

var remove;
if (keysToRemove instanceof function) {
      remove = keysToRemove; 
    } else {
      var keysToRemoveArray = (keysToRemove instanceof Array) ? 
         keysToRemove : Array.prototype.slice.call(arguments);
      remove = function(val, key) { return keysToRemoveArray.indexOf(key) >= 0; }
    }

    var result = this.instantiateEmptyObject();

    for (var key in this) {
      if (this.hasOwnProperty(key) && ! remove(this[key], key)) {
        result[key] = this[key];
      }
    }

But you are of course the arbiter of the usefulness vs. cost :-)

rtfeldman commented 8 years ago

Okay, I'm on board. Happy to accept a PR for this!

rtfeldman commented 8 years ago

Implemented! Will release as 5.1.0 once cross-browser tests pass.