immutable-ops
A collection of functions to perform immutable operations on plain JavaScript objects and arrays. Aims to have the simplicity and small size of seamless-immutable
, but with a functional, curried API, no special immutable object type, and batched mutations.
Features
- Small. It's just 10 functions.
- Functional API with curried functions
- JavaScript in, JavaScript out
- Batched mutations
Installation
npm install immutable-ops
Usage
import compose from 'ramda/src/compose';
import getImmutableOps from 'immutable-ops';
const {
merge,
mergeDeep,
omit,
setIn,
insert,
splice,
push,
filter,
set,
batched,
__,
} = getImmutableOps({
curried: true
});
const person = {
name: 'Tommi',
age: 25,
location: {
city: 'New York',
country: 'US',
},
};
const moveToNY = mergeDeep({ location: { city: 'New York' }});
const moveToSF = mergeDeep({ location: { city: 'San Francisco' }});
const updatedPerson = moveToNY(person);
updatedPerson === person
const becomeABanker = setIn('occupation.title', 'Investment Banker');
const advanceCareer = compose(becomeABanker, moveToNY)
const personWithJob = advanceCareer(person);
console.log(personWithJob === person);
console.log(personWithJob);
const runOperations = compose(advanceCareer, moveToSf);
const personWithJobTwo = ops.batched(() => runOperations(person));
console.log(person === personWithJobTwo)
console.log(personWithJobTwo);
Batched Mutations
You can batch operations by calling ops.batched(func)
with a function.
When immutable-ops
creates a new object or array during batched mutations to preserve immutability, it tags it as a mutable object (by adding an unenumerable @@_____canMutate
property) and pushes its reference to an array of mutatedObjects
. All consecutive functions applied will execute a mutating operations for objects that have the tag. This applies for tagged objects found in nested structures too.
When the function finishes executing, immutable-ops
loops through the mutatedObjects
array, removing the tag properties from each object, and clearing the mutatedObjects
array.
Currying
All operations are curried by default. If you don't want them to be curried, pass { curried: false }
to getImmutableOps()
. Functions are curried with ramda.curry
. In addition to normal currying behaviour, you can use the ramda
placeholder variable available in ops.__
to specify parameters you want to pass arguments for later. Example:
const removeNFromHead = ops.splice( 0, ops.__, []);
const removeTwoFromHead = removeNFromHead(2);
const arr = [1, 2, 3];
console.log(removeTwoFromHead(arr));
Object API
merge(mergeObj, targetObj)
Performs a shallow merge on targetObj
. mergeObj
can be a single object to merge, or a list of objects. If a list is passed as mergeObj
, objects to the right in the list will have priority when determining final attributes.
Returns the merged object, which will be a different object if an actual change was detected during the merge.
const result = ops.merge(
{
a: 'theA',
b: {
c: 'nestedC',
},
},
{
a: 'theA2',
b: {
d: 'nestedD',
},
c: 'theC',
}
);
console.log(result);
deepMerge(mergeObj, targetObj)
Same as merge
, but performs merge
recursively on attributes that are objects (not arrays).
const result = ops.deepMerge(
{
a: 'theA',
b: {
c: 'nestedC',
},
},
{
a: 'theA2',
b: {
d: 'nestedD',
},
c: 'theC',
}
);
console.log(result);
setIn(path, value, targetObj)
Returns an object, with the value at path
set to value
. path
can be a dot-separated list of attribute values or an array of attribute names to traverse.
const obj = {
location: {
city: 'San Francisco',
},
};
const newObj = ops.setIn(['location', 'city'], 'Helsinki', obj);
console.log(newObj);
omit(keysToOmit, targetObj)
Returns a shallow copy of targetObj
without the keys specified in keysToOmit
. keysToOmit
can be a single key name or an array of key names.
const obj = {
a: true,
b: true,
};
const result = ops.omit('a', obj);
console.log(result);
Array API
insert(startIndex, values, targetArray)
Returns a new array with values
inserted at starting at index startIndex
to targetArray
.
const arr = [1, 2, 4];
const result = ops.insert(2, [3], arr);
console.log(result);
push(value, targetArray)
Returns a shallow copy of targetArray
with value
added to the end. value
can be a single value or an array of values to push.
const arr = [1, 2, 3];
const result = ops.push(4, arr);
console.log(result);
filter(func, targetArray)
Returns a shallow copy of targetArray
with items that func
returns true
for, when calling it with the item.
const arr = [1, 2, 3, 4];
const result = ops.filter(item => item % 2 === 0, arr);
console.log(result);
splice(startIndex, deleteCount, values, targetArray)
Like Array.prototype.splice
, but operates on a shallow copy of targetArray
and returns the shallow copy.
const arr = [1, 2, 3, 3, 3, 4];
const result = ops.splice(2, 2, [], arr);
console.log(result);
API for both Object and Array
set(key, value, target)
Returns a shallow copy of target
with its value at index or key key
set to value
.
const arr = [1, 2, 5];
const result = ops.set(2, 3, arr);
console.log(result);
const obj = {
a: 'X',
b: 'theB',
};
const resultObj = ops.set('a', 'theA', obj);
console.log(resultObj);
License
MIT. See LICENSE