Socket
Socket
Sign inDemoInstall

immutable-set

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

immutable-set - npm Package Compare versions

Comparing version 1.1.0 to 2.0.0

132

lib/set.js

@@ -19,2 +19,10 @@ 'use strict';

/**
* Recursive equality tester. Return true if the top down element of the path in the base is equal to the given value
* @param base current base
* @param path current path
* @param value value to verify
* @param equality override the equality assertion
* @returns {boolean} true if the value is in the base at the end of the path
*/
function recursiveEqual(base, path, value, equality) {

@@ -36,2 +44,92 @@ if (!base || (typeof base === 'undefined' ? 'undefined' : _typeof(base)) !== 'object') {

/**
* Compute next base in recursion
* @param base current base
* @param key current key in the path
* @param nextKey next key in the path
* @param withArrays create arrays instead of object when nextKey is a number and key has no value in the current base
*/
var nextBase = function nextBase(base, key, nextKey, withArrays) {
return base[key] || (withArrays && typeof nextKey === 'number' ? [] : {});
};
/**
* Reduce multiple elements in the accumulator using the setFunction when base is an Array
* @param setFunction
*/
var reduceWithArray = function reduceWithArray(setFunction) {
return (
/**
* Reduce multiple elements in the accumulator
* @param base current base
* @param path current path
* @param keys list of keys ; we know there that keys is an array or set
* @param nextKey next key in the path
* @param values list of values, has to be an array
* @param withArrays
* @param accumulator new instance of the array at the current level
* @returns {*} the accumulator with the new elements
*/
function (base, path, keys, nextKey, values, withArrays, accumulator) {
if (!Array.isArray(values)) {
throw new Error('Can not use object values with array in path');
}
return keys.reduce(function (acc, key, index) {
var newValue = setFunction(nextBase(base, key, nextKey, withArrays), path.slice(1), values[index], withArrays);
if (key < acc.length) {
acc[key] = newValue;
} else {
acc.push(newValue);
}
return acc;
}, accumulator);
}
);
};
/**
* Reduce multiple elements in an new Object using the setFunction when base is an Object
* @param setFunction
*/
var reduceWithObject = function reduceWithObject(fn) {
return (
/**
* Reduce multiple elements in an new Object
* @param base current base
* @param path current path
* @param keys list of keys ; we know there that keys is an array or set
* @param nextKey next key in the path
* @param values list of values, could be an array or an object
* @param withArrays
* @returns {*} a set of the new elements
*/
function (base, path, keys, nextKey, values, withArrays) {
if (Array.isArray(values)) {
return keys.reduce(function (acc, key, index) {
acc[key] = fn(nextBase(base, key, nextKey, withArrays), path.slice(1), values[index], withArrays);
return acc;
}, {});
}
return keys.reduce(function (acc, key) {
acc[key] = fn(nextBase(base, key, nextKey, withArrays), path.slice(1), values[key], withArrays);
return acc;
}, {});
}
);
};
/**
* Recursive immutable set
* @param base current base
* @param path current path
* @param value current value
* @param withArrays create arrays instead of object when nextKey is a number and key has no value in the current base
* @returns {*} a new instance of the given level
*/
function set(base, path, value, withArrays) {

@@ -46,20 +144,42 @@ if (path.length === 0) {

var isArrayKeys = Array.isArray(key);
var currentBase = base;
if (!base || (typeof base === 'undefined' ? 'undefined' : _typeof(base)) !== 'object') {
currentBase = withArrays && typeof key === 'number' ? [] : {};
currentBase = isArrayKeys && typeof key[0] === 'number' || withArrays && typeof key === 'number' ? [] : {};
}
if (isArrayKeys) {
if (Array.isArray(currentBase)) {
return reduceWithArray(set)(currentBase, path, key, nextKey, value, withArrays, [].concat(_toConsumableArray(currentBase)));
}
return _extends({}, currentBase, reduceWithObject(set)(currentBase, path, key, nextKey, value, withArrays, {}));
}
if (Array.isArray(currentBase)) {
return [].concat(_toConsumableArray(currentBase.slice(0, key)), [set(currentBase[key] || (withArrays && typeof nextKey === 'number' ? [] : {}), path.slice(1), value, withArrays)], _toConsumableArray(currentBase.slice(key + 1)));
return [].concat(_toConsumableArray(currentBase.slice(0, key)), [set(nextBase(currentBase, key, nextKey, withArrays), path.slice(1), value, withArrays)], _toConsumableArray(currentBase.slice(key + 1)));
}
return _extends({}, currentBase, _defineProperty({}, key, set(currentBase[key] || (withArrays && typeof nextKey === 'number' ? [] : {}), path.slice(1), value, withArrays)));
return _extends({}, currentBase, _defineProperty({}, key, set(nextBase(currentBase, key, nextKey, withArrays), path.slice(1), value, withArrays)));
}
/**
* Main function, recursive immutable set
* @param base base object
* @param initialPath path to the place where the value is going to be set
* @param value value to set
* @param options options
* @returns {*} new instance of the base if it has been modified, the base otherwise
*/
function safeSet(base, initialPath, value) {
var withArrays = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var equality = arguments[4];
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
var _options$withArrays = options.withArrays,
withArrays = _options$withArrays === undefined ? false : _options$withArrays,
equality = options.equality,
safe = options.safe;
var path = initialPath;
// Handle string path
if (typeof path === 'string' && path.length > 0) {

@@ -85,3 +205,3 @@ path = path.split('.');

// If the value is already here we just need to return the base
if (recursiveEqual(base, path, value, equality)) {
if (safe && recursiveEqual(base, path, value, equality)) {
return base;

@@ -88,0 +208,0 @@ }

2

package.json
{
"name": "immutable-set",
"version": "1.1.0",
"version": "2.0.0",
"description": "Set nested properties of an object while respecting the principles of immutability",

@@ -5,0 +5,0 @@ "main": "lib/set.js",

@@ -32,10 +32,17 @@ ![build status](https://travis-ci.org/M6Web/immutable-set.svg)

name | description | type | requiered
---- | ----------- | ---- | ---------
base | object to modify | object | ✅
path | list of keys to access the value to being modified | array or string | ✅
value | value to set | any | ✅
options | options... | object
### Options
name | description | type | default
---- | ----------- | ---- | -------
base | object to modify | object
path | list of keys to access the value to being modified | array or string
value | value to set | any
withArray (optional)| if set to `true` number will be interpreted has array indexes | boolean | false
equality (optional) | if provided, the function will be used to determine if the value at the path is equal to the value provided | function | `===`
withArray | if set to `true` number will be interpreted has array indexes | boolean | false
equality | if provided, the function will be used to determine if the value at the path is equal to the value provided | function | `===`
safe | verify if the value does not already exist | boolean | false
## Usage

@@ -51,12 +58,10 @@ Import `set` function

```js
const newState = set(state, ['a', 0, 'b'], 42, true);
const newState = set(state, ['a', 'b'], 42);
// or
const newState = set(state, 'a[0].b', 42, true);
const newState = set(state, 'a.b', 42);
/*
newState => {
a: [
{
b: 42,
},
],
a: {
b: 42,
},
...

@@ -67,2 +72,48 @@ }

The function mutates the object only if the value is not already present in the object
The function mutates the object only if the value is not already present in the object.
## Advanced usage
### with arrays
The option `withArrays` allow to dynamically create arrays when the current level is empty and the current path key is a number.
```js
let base = set({}, ['a', 0], 12, { withArrays: true });
// will return { a: [12] }
```
### safe
The option `safe` will verify if the value is not already in the object.
```js
const base = { a: 2 };
set(base, 'a', 2, { safe: true })
// will return the base unmodified
```
### equality
The option `equality` allow to use an other equality function instead of `===`. It has to be used with `safe` option.
```js
const base = { a: { id: 1, v: 0 } };
const equality = (a, b) => a.id === b.id && a.v === b.v };
set(base, 'a', { id: 1, v: 0 }, { safe: true, equality);
// will return the base unmodified
set(base, 'a', { id: 1, v: 1 }, { safe: true, equality);
// will return { a: { id: 1, v: 1 } }
```
### multiple set
It is possible to set multiple elements at once, providing multiple keys in the path and an array (or an object) in value.
```js
set({}, ['a', ['b', 'c']], [12, 13]);
// or
set({}, ['a', ['b', 'c']], { b: 12, c: 13 });
// will return { a: { b: 12, c: 13 } }
set({}, ['a', [0, 1 ]], [12, 13], { withArrays: true });
// will return { a: [12, 13] }
```
- :warning: If the array of keys is not the last element of the path, the rest of the path will be used for each sub tree.
- :warning: It's not possible to set objects in array with object sub values, this will throw an error.
- :warning: For now safe mode does not work with multiple set.

@@ -0,1 +1,9 @@

/**
* Recursive equality tester. Return true if the top down element of the path in the base is equal to the given value
* @param base current base
* @param path current path
* @param value value to verify
* @param equality override the equality assertion
* @returns {boolean} true if the value is in the base at the end of the path
*/
function recursiveEqual(base, path, value, equality) {

@@ -14,2 +22,84 @@ if (!base || typeof base !== 'object') {

/**
* Compute next base in recursion
* @param base current base
* @param key current key in the path
* @param nextKey next key in the path
* @param withArrays create arrays instead of object when nextKey is a number and key has no value in the current base
*/
const nextBase = (base, key, nextKey, withArrays) => base[key] || (withArrays && typeof nextKey === 'number' ? [] : {});
/**
* Reduce multiple elements in the accumulator using the setFunction when base is an Array
* @param setFunction
*/
const reduceWithArray = setFunction =>
/**
* Reduce multiple elements in the accumulator
* @param base current base
* @param path current path
* @param keys list of keys ; we know there that keys is an array or set
* @param nextKey next key in the path
* @param values list of values, has to be an array
* @param withArrays
* @param accumulator new instance of the array at the current level
* @returns {*} the accumulator with the new elements
*/
(base, path, keys, nextKey, values, withArrays, accumulator) => {
if (!Array.isArray(values)) {
throw new Error('Can not use object values with array in path');
}
return keys.reduce((acc, key, index) => {
const newValue = setFunction(nextBase(base, key, nextKey, withArrays), path.slice(1), values[index], withArrays);
if (key < acc.length) {
acc[key] = newValue;
} else {
acc.push(newValue);
}
return acc;
}, accumulator);
};
/**
* Reduce multiple elements in an new Object using the setFunction when base is an Object
* @param setFunction
*/
const reduceWithObject = fn =>
/**
* Reduce multiple elements in an new Object
* @param base current base
* @param path current path
* @param keys list of keys ; we know there that keys is an array or set
* @param nextKey next key in the path
* @param values list of values, could be an array or an object
* @param withArrays
* @returns {*} a set of the new elements
*/
(base, path, keys, nextKey, values, withArrays) => {
if (Array.isArray(values)) {
return keys.reduce((acc, key, index) => {
acc[key] = fn(nextBase(base, key, nextKey, withArrays), path.slice(1), values[index], withArrays);
return acc;
}, {});
}
return keys.reduce((acc, key) => {
acc[key] = fn(nextBase(base, key, nextKey, withArrays), path.slice(1), values[key], withArrays);
return acc;
}, {});
};
/**
* Recursive immutable set
* @param base current base
* @param path current path
* @param value current value
* @param withArrays create arrays instead of object when nextKey is a number and key has no value in the current base
* @returns {*} a new instance of the given level
*/
function set(base, path, value, withArrays) {

@@ -21,12 +111,24 @@ if (path.length === 0) {

const [key, nextKey] = path;
const isArrayKeys = Array.isArray(key);
let currentBase = base;
let currentBase = base;
if (!base || typeof base !== 'object') {
currentBase = withArrays && typeof key === 'number' ? [] : {};
currentBase = (isArrayKeys && typeof key[0] === 'number') || (withArrays && typeof key === 'number') ? [] : {};
}
if (isArrayKeys) {
if (Array.isArray(currentBase)) {
return reduceWithArray(set)(currentBase, path, key, nextKey, value, withArrays, [...currentBase]);
}
return {
...currentBase,
...reduceWithObject(set)(currentBase, path, key, nextKey, value, withArrays, {}),
};
}
if (Array.isArray(currentBase)) {
return [
...currentBase.slice(0, key),
set(currentBase[key] || (withArrays && typeof nextKey === 'number' ? [] : {}), path.slice(1), value, withArrays),
set(nextBase(currentBase, key, nextKey, withArrays), path.slice(1), value, withArrays),
...currentBase.slice(key + 1),

@@ -38,14 +140,19 @@ ];

...currentBase,
[key]: set(
currentBase[key] || (withArrays && typeof nextKey === 'number' ? [] : {}),
path.slice(1),
value,
withArrays,
),
[key]: set(nextBase(currentBase, key, nextKey, withArrays), path.slice(1), value, withArrays),
};
}
export default function safeSet(base, initialPath, value, withArrays = false, equality) {
/**
* Main function, recursive immutable set
* @param base base object
* @param initialPath path to the place where the value is going to be set
* @param value value to set
* @param options options
* @returns {*} new instance of the base if it has been modified, the base otherwise
*/
export default function safeSet(base, initialPath, value, options = {}) {
const { withArrays = false, equality, safe } = options;
let path = initialPath;
// Handle string path
if (typeof path === 'string' && path.length > 0) {

@@ -71,3 +178,3 @@ path = path.split('.');

// If the value is already here we just need to return the base
if (recursiveEqual(base, path, value, equality)) {
if (safe && recursiveEqual(base, path, value, equality)) {
return base;

@@ -74,0 +181,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc