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

.without() on nested objects #207

Open guyellis opened 7 years ago

guyellis commented 7 years ago

Can seamless do something like this? If so, how do you do it?

var a = Immutable({x: {y:1, z:2}})
a.without('x')
// {}
a.without('x.y')
// Actual: { x: { y: 1, z: 2 } }
// Expected/Desired: { x: { z: 2 } }
cdomigan commented 7 years ago

I think the only way currently is:

var a = Immutable({x: {y:1, z:2}});
a.update('x', x => x.without('y'));
// {x: {z:2}}

An equivalent to Immutable.JS deleteIn() would be nice!

raduflp commented 7 years ago

I would suggest to make it consistent with setIn and updateIn, i.e. using arrays for nested objects paths something like a.without(['lvl1_key', 'lvl2_key', ...])

currently the syntax above has the same behaviour as a.without('lvl1_key1', 'lvl1_key2' ... ) I see little value in having 2 ways of achieving the same effect and adds to the confusion when considering the syntax for setIn etc

maybe implementing a withoutIn would be a solution to avoid breaking changes

Aaronius commented 6 years ago

@rtfeldman is this something you would accept a pull request for?

Aaronius commented 6 years ago

One common painpoint we run into because we don't have a withoutIn() can be seen here:

let a = Immutable({});
if (a.getIn(['a', 'b', 'c'])) {
  a = a.updateIn(['a', 'b', 'c'], (c) => {
    return c.without('d');
  });
}

If we don't do the if statement, the a.b.c chain will get created when we do an updateIn(), which isn't what we want. It would be so much simpler to do:

let a = Immutable({});
a = a.withoutIn(['a', 'b', 'c', 'd']);
seveibar commented 4 years ago

Simple withoutIn implementation:

function withoutIn(obj, path) {
  if (path.length === 0) return without(obj, path[0])
  const parentPath = path.slice(0, -1)
  const key = path[path.length - 1]
  return setIn(obj, parentPath, without(getIn(obj, parentPath), key))
}
withoutIn({"a": {"b": {"c": 1, "d": 2}}}, ["a","b","d"])
// { "a": { "b": { "c": 1 } } }