Reprinted fromImmutable.js

Immutable.js

Handling JavaScript complex objects: deep copy, Immutable & Immer

This article is just a brief introduction to Immutable. We will continue to share the practical application of Immutable.

immutable.jpeg

What is Immutable Data?

Immutable data encourages pure functions (data-in, data-out) and lends itself to much simpler application development and enabling techniques from functional programming such as lazy evaluation.

— Official document description

Immutable Data is Data that, once created, cannot be changed. Any modification or addition or deletion of an Immutable object returns a new Immutable object. Immutable is a Persistent Data Structure, which ensures that old Data can be used to create new Data without changing it. And to avoid the performance cost of deepCopy copying all nodes once, Immutable uses Structural Sharing, where if a node in the object tree changes, only that node and its affected parent are modified, and the other nodes are shared. Watch the animation below:

Structure of Shared

Immutable has advantages and disadvantages

advantages

1. Reduce the complexity of Mutable

Shared mutable state is the root of all evil. A simple example is reference assignment in JS:

var obj = { a: 1 }; var copy_obj = obj; copy_obj.a = 2; console.log(obj.a); / / 2Copy the code

Reference assignment can save memory, but mutable state can become a nightmare when the application is complex. It is common to use shallowCopy or deepCopy to avoid modification, but this can cause CPU and memory consumption, but Immulate is a good solution to these problems.

2. Save memory space

As mentioned above, Immutable. Js uses this method to reuse as much memory as possible, and even objects previously used can be reused. Objects that are not referenced are garbage collected.

import { Map } from'immutable';
let a = Map({
  select: 'users',
  filter: Map({ name: 'Cam' })
})
let b = a.set('select', 'people');

a === b; // false
a.get('filter') === b.get('filter'); // true
Copy the code

Above, A and B share unchanged filter nodes.

3. Undo/Redo, Copy/Paste,

Because the data is different each time, it’s easy to develop undo redo by simply storing the data in an array and pulling it out wherever you want to go.

4. Embrace functional programming

Immutable (persistent data structures) is itself a concept in functional programming. Functional programming is concerned with the mapping of data, imperative programming is concerned with the steps to solve the problem, and pure functional programming is more suitable for front-end development than object-oriented programming. Because as long as the inputs are consistent, the outputs must be consistent, developing components is easier to debug and assemble.

disadvantages

Aside from the cost of learning and the additional resource files introduced, let’s take a look at some of the frustrations.

1. Easy to mix with native objects

The mainly Immutable API is designed to be similar to native objects and is easy to confuse operations. For example, the Map and List operations:

// Immutableconstmap = Map({ a: 1, b: 2 }); Constlist = List ([1, 2, 3]); // native jsconst obj = {a: 1, b: 2}; Const 'arry = [1, 2, 3]; // Compare console.log(map.get('a')); console.log(list.get(0)); console.log(obj.a); console.log(arry[0]);Copy the code

Immutable. Introduction of js

It took Facebook engineer Lee Byron three years to build and came out at the same time as React, but wasn’t included in the React toolkit by default (React provides a simplified Helper). It internally implements a complete set of Persistent Data Structure, as well as many easy-to-use Data types. Like Collection, List, Map, Set, Record, Seq. There are very comprehensive map, filter, groupBy, reduce ‘ ‘find function operation methods. Also, the API is as similar to Object or Array as possible.

immutablejs-getters-and-setters-everywhere.jpg

Several data types of Immutable

  • List: An ordered set of indexes, similar to an Array in JavaScript.
  • Map: An unordered set of indexes, similar to JavaScript objects.
  • OrderedMap: an OrderedMap, sorted by the set() of the data.
  • Set: A Set with no duplicate values.
  • OrderedSet: an OrderedSet, sorted according to the add of data.
  • Stack: Ordered collection, which can be added and deleted using unshift() and shift().
  • Record: A class that generates an instance of Record. Object is similar to JavaScript Object, but only accepts a specific string as key, with a default value.
  • Seq: sequence, but may not be supported by specific data structures.
  • Collection: is the base class for building all data structures and cannot be built directly.

The most commonly used data types are lists and maps, so I’ll focus on the apis for those two data types here.

A common API for Immutable. Js

fromJS()

Function: To convert JS data to Immutable data

FromJS (value, Converter)

Summary: Value is the data to convert, converter is the operation to do. The second parameter is optional. By default, arrays are converted to List and objects are converted to Map

Code implementation:

const obj = Immutable.fromJS({a:'123',b:'234'},function (key, value, path) {
        console.log(key, value, path)
        return isIndexed(value) ? value.toList() : value.toOrderedMap())
    })
Copy the code
toJS()

Function: To convert an Immutable data to JS data

Usage: value. ToJS ()

is()

Function: Compares two objects

Usage: is (map1, map2)

HashCode and valueOf are the same value as long as the hashcodes of two objects are equal. This method is used to improve performance and avoid deep iterating

Code implementation:

import { Map, is } from'immutable'const map1 = Map({ a: 1, b: 1, c: 1 })
const map2 = Map({ a: 1, b: 1, c: 1 })
map1 === map2   //falseObject.is(map1, map2) // false
is(map1, map2) // true
Copy the code
The List () and Map ()

Creates a new List/Map object

Usage:

//List Immutable.List(); // Empty List Immutable.List([1, 2]); //Map Immutable.Map(); // Empty Map Immutable.Map({a: '1', b: '2'});Copy the code
List. IsList () and Map. IsMap ()

Function: Check whether a data structure is of type List/Map

Usage:

List.isList([]); // false
List.isList(List()); // trueMap.isMap({}) // falseMap.isMap(Map()) // true
Copy the code
size

Action: property to get the length of List/Map, equivalent to immutableData.count ();

The get (), getIn ()

Gets data from a data structure

Immutabledata.get (0); Immutabledata.get ('a'); // Get data from a nested array immutableData.getin ([1, 2]); Immutabledata. getIn(['a', 'b']);Copy the code
From the (), hasIn ()

Function: Checks whether a key exists

Usage:

Immutable. FromJS ([1, 2, 3, 5} {a: 4, b:]). From the (' 0 '); / / true Immutable. FromJS ([1, 2, 3, 5} {a: 4, b:]). From the (' 0 '); / / true Immutable. FromJS ([1, 2, 3, 5} {a: 4, b:]). HasIn ([3, 'b']) / / trueCopy the code
includes()

Function: Check whether a value exists

Usage:

Immutable. FromJS ([1, 2, 3, 5} {a: 4, b:]). Includes (2); / / true Immutable. FromJS ([1, 2, 3, 5} {a: 4, b:]). Includes (' 2 '); //false does not contain character 2 Immutable. FromJS ([1,2,3,{a:4,b:5}]). / / false Immutable. FromJS ([1, 2, 3, 5} {a: 4, b:]). Includes ({5} a: 4, b:) / / false Immutable. FromJS ([1, 2, 3, 5} {a: 4, b:]). Includes (Immutable) fromJS ({5} a: 4, b:)) / / trueCopy the code
The first () and last ()

Function: Gets the first or last element, or returns undefined

Code:

Immutable. FromJS ([1, 2, 3, 5} {a: 4, b:]). The first () / / 1 Immutable. FromJS ([1, 2, 3, 5} {a: 4, b:]). The last () / / {5} a: 4, b: Immutable.fromJS({a:1,b:2,c:{d:3,e:4}}).first() //1 Immutable.fromJS({a:1,b:2,c:{d:3,e:4}}).first() //{d:3,e:4}Copy the code

Data modification

Note: An Immutable data type is an Immutable data type by which a value is assigned to a new data.

Set the set ()

Function: Set the key and index values of the first layer

Usage:

const originalList = List([ 0 ]);
// List [ 0 ]
originalList.set(1, 1);
// List [ 0, 1 ]
originalList.set(0, 'overwritten');
// List [ "overwritten" ]
originalList.set(2, 2);
// List [ 0, undefined, 2 ]List().set(50000, 'value').size;
// 50001const originalMap = Map()
const newerMap = originalMap.set('key', 'value')
const newestMap = newerMap.set('key', 'newer value')

originalMap
// Map {}
newerMap
// Map { "key": "value" }
newestMap
// Map { "key": "newer value" }
Copy the code

When used, set index to number to value. When Map is used, the key value is set to value.

When the number passed in the List is negative, the size+index value is set to value. For example, if the number passed in is -1, the size-1 value is set to value. If the number value passed in exceeds the length of the List, the List is automatically completed as the value of the number passed in, and the number is set to value. [,,,] : void exists in the List. If there is no value in the List, undefined.

setIn()

Function: Sets the value of an attribute in a deep structure

Usage:

const originalMap = Map({ subObject: Map({ subKey: 'subvalue', subSubObject: Map({ subSubKey: 'subSubValue' }) }) }) const newMap = originalMap.setIn(['subObject', 'subKey'], 'ha ha! ') // Map {// "subObject": Map {// "subKey": "ha ha!" ,// "subSubObject": Map { "subSubKey": "subSubValue" }// }// }const newerMap = originalMap.setIn( ['subObject', 'subSubObject', 'subSubKey'], 'ha ha ha! ' ) // Map {// "subObject": Map {// "subKey": "subvalue",// "subSubObject": Map { "subSubKey": "ha ha ha!" } / / / /}}Copy the code

The usage is the same as set(), except that the first argument is an array representing the location of the property to be set

Delete the delete

Function: Deletes attributes in a layer 1 structure

Usage:

// List
List([ 0, 1, 2, 3, 4 ]).delete(0);
// List [ 1, 2, 3, 4 ]// Mapconst originalMap = Map({
  key: 'value',
  otherKey: 'other value'
})
// Map { "key": "value", "otherKey": "other value" }
originalMap.delete('otherKey')
// Map { "key": "value" }
Copy the code
deleteIn()

Used to delete deep data, see setIn for usage

DeleteAll () (Map only, List not)

Function: Deletes multiple keys from a Map

Usage: deleteAll(keys: Iterable): this

Code examples:

const names = Map({ a: "Aaron", b: "Barry", c: "Connor" })
names.deleteAll([ 'a', 'c' ])
// Map { "b": "Barry" }
Copy the code
Update the update ()

Function: To update an attribute in an object. You can perform related operations on the original data

Usage:

////Listconstlist = List([ 'a', 'b', 'c' ])
const result = list.update(2, val => val.toUpperCase())

///Mapconst aMap = Map({ key: 'value' })
const newMap = aMap.update('key', value => value + value)
Copy the code
updateIn()

For usage, see setIn

Remove the clear ()

Action: Clears all data

Usage: clear(): this

Code examples:

Map({ key: 'value'}).clear() //MapList([1, 2, 3, 4]).clear() Like push, pop, Shift, unshift, insert.Copy the code
push()

Insert an element at the end of the List

pop()

Delete an element at the end of the List

unshift

Insert an element at the head of the List

shift

Delete an element at the head of the List

insert

Insert element at index of List

Code implementation:

List([ 0, 1, 2, 3, 4 ]).insert(6, 5) 
//List [ 0, 1, 2, 3, 4, 5 ]List([ 1, 2, 3, 4 ]).push(5)
// List [ 1, 2, 3, 4, 5 ]List([ 1, 2, 3, 4 ]).pop()
// List[ 1, 2, 3 ]List([ 2, 3, 4]).unshift(1);
// List [ 1, 2, 3, 4 ]List([ 0, 1, 2, 3, 4 ]).shift();
// List [ 1, 2, 3, 4 ]
Copy the code

There’s also a special way to set the length of a List, setSize()

List([]).setSize(2).toJS() //[undefined,undefined]
Copy the code

About the merge

merge

Functions: shallow merge, compare the new data with the old data, add the attributes that do not exist in the old data directly, and overwrite the existing attributes in the new data

mergrWith

Function: custom shallow merge, you can set the value of some attributes

mergeIn

Function: Shallow merge of deep data

mergeDeep

Function: Deep merge. The attributes existing in the new and old data are the data after the merge of the new and old data

mergeDeepIn

Function: Deep merge deep data

mergrDeepWith

Function: Custom deep merge, you can set the value of some properties

Merge merge merge merge merge Merge Merge Merge Merge Merge Merge Merge Merge Merge

const Map1 = Immutable.fromJS({a:111,b:222,c:{d:333,e:444}});
 const Map2 = Immutable.fromJS({a:111,b:222,c:{e:444,f:555}});

 const Map3 = Map1.merge(Map2);
  //Map {a:111,b:222,c:{e:444,f:555}}const Map4 = Map1.mergeDeep(Map2);
  //Map {a:111,b:222,c:{d:333,e:444,f:555}}const Map5 = Map1.mergeWith((oldData,newData,key)=>{
      if(key === 'a'){
        return666;
      }else{
        return newData
      }
    },Map2);
  //Map {a:666,b:222,c:{e:444,f:555}}
Copy the code

Sequence algorithm

concat()

Function: Concatenation of objects, used in the same way as concat() in js arrays, returns a new object.

Const List = list1.concat(list2)

map()

Returns a new object by iterating over the entire object, performing operations on the Map/List elements.

Usage:

Map({a:1,b:2}).map(val=>10*val)
//Map{a:10,b:20}
Copy the code
MapKey ()

Returns a new object by iterating over the entire object, operating on the key of the Map element.

Usage:

Map({a:1,b:2}).mapKey(val=>val+'l')
//Map{al:10,bl:20}
Copy the code
Map unique mapEntries()

Returns a new object by iterating over the entire object, operating on both the key and the value of the Map element. Map () of Map can also do this.

Usage:

Map({a:1,b:2}).map((key,val)=>{
  return [key+'l',val*10]
})
//Map{al:10,bl:20}
Copy the code
Filter filter

Function: Returns a new object containing all elements that meet the filter criteria

Usage:

Map({a:1,b:2}).filter((key,val)=>{
  return val == 2
})
//Map{b:2}
Copy the code

There is also a filterNot() method, which is the opposite.

Inversion of reverse

Function: Reverses the data structure

Code examples:

Immutable.fromJS([1, 2, 3, 4, 5]).reverse(); / / the List,4,3,2,1 [5] Immutable. FromJS ({a: 1, b: {2, c: d: 3}, e: 4}). The recerse (); //Map {e:4,b:{c:2,d:3},a:1}Copy the code
Sort sort & sortBy

Function: Sorts data structures

Code examples:

/ / / the List Immutable. FromJS (,3,5,2,6,1 [4]). The sort () / / the List [6] Immutable. FromJS (,3,5,2,6,1 [4]). Sort ((a, b) = > {the if (a  < b) { return-1; } if (a > b) { return1; } if (a === b) { return0; }}) / / the List [6] Immutable. FromJS ([{a: 3}, {a: 2}, {a: 4}, {a: 1}]). The sortBy ((val, index, obj) = > {return val. Get (' a ') },(a,b)=>{ if (a < b) { return-1; } if (a > b) { return1; } if (a === b) { return0; } }) //List [ {a:3}, {a:2}, {a:4}, {a:1} ]//Map Immutable.fromJS( {b:1, a: 3, c: 2, d:5} ).sort() //Map {b: 1, c: 2, a: 3, d: 5} Immutable.fromJS( {b:1, a: 3, c: 2, d:5} ).sort((a,b)=>{ if (a < b) { return-1; } if (a > b) { return1; } if (a === b) { return0; } }) //Map {b: 1, c: 2, a: 3, d: 5} Immutable.fromJS( {b:1, a: 3, c: 2, d:5} ).sortBy((value, key, obj)=> { return value }) //Map {b: 1, c: 2, a: 3, d: 5}Copy the code
Grouping groupBy

Function: Group data

const listOfMaps = List([
  Map({ v: 0 }),
  Map({ v: 1 }),
  Map({ v: 1 }),
  Map({ v: 0 }),
  Map({ v: 2 })
])
const groupsOfMaps = listOfMaps.groupBy(x => x.get('v'))
// Map {//   0: List [ Map{ "v": 0 }, Map { "v": 0 } ],//   1: List [ Map{ "v": 1 }, Map { "v": 1 } ],//   2: List [ Map{ "v": 2 } ],// }
Copy the code
To find the data

IndexOf () and lastIndexOf Map do not exist

Returns the index of the first or last value, or -1

Usage:

Immutable. FromJS ([1, 2, 3, 4]) indexof (3) / / 2 Immutable. FromJS ([1, 2, 3, 4]). LastIndexof (3) / / 2Copy the code
FindIndex () and findLastIndex() Map do not have this method

Function: Find the index value of an element that meets the requirement

Usage:

Immutable.fromjs ([1,2,3,4]).findindex ((value,index,array)=>{return value%2 === 0; }) // 1 immutable.fromjs ([1,2,3,4]).findLastIndex((value,index,array)=>{return index%2 === 0; }) / / 3Copy the code
The find (), findLast ()

Finds the value of an element that meets the condition

Usage:

FromJS ([1,2,3,4]). Find ((value,index,array)=>{return value%2 === 0; }) // 2 immutable.fromjs ([1,2,3,4]).findlast ((value,index,array)=>{return value%2 === 0; }) / / 4Copy the code
FindKey (), findLastKey ()

Finds the key of an element that meets the criteria

Usage:

Immutable.fromjs ([1,2,3,4]).findkey ((value,index,array)=>{return value%2 === 0; }) // 1 immutable.fromjs ([1,2,3,4]).findLastKey((value,index,array)=>{return value%2 === 0; }) / / 3Copy the code
FindEntry (), findLastEntry ()

Search for key:value pairs of elements that meet the criteria

Usage:

Immutable. FromJS ([1,2,3,4]). FindEntry ((value,index,array)=>{return value%2 === 0; }) // [1,2] Immutable.fromjs ([1,2,3,4]).findlastentry ((value,index,array)=>{return value%2 === 0; }) / / [3, 4]Copy the code
keyOf() lastKeyOf()

Function: Searches for the key corresponding to a value

Usage:

Immutable. FromJS ([1, 2, 3, 4]) keyOf (2) / / 1 Immutable. FromJS ([1, 2, 3, 4]). LastKeyOf (2) / / 1Copy the code
Max (), maxBy ()

Function: Find the maximum value

Usage:

Immutable.fromJS([1, 2, 3, 4]).max() //4

Immutable.fromJS([{a;1},{a:2},{a: 3},{a:4}]).maxBy((value,index,array)=>{
  return value.get('a')
})  //{a:4}
Copy the code
The min (), minBy ()

Function: Find the minimum value

Usage:

Immutable.fromJS([1, 2, 3, 4]).min() //1

Immutable.fromJS([{a;1},{a:2},{a: 3},{a:4}]).minBy((value,index,array)=>{
  return value.get('a')
})  //{a:1}
Copy the code

Create a subset

slice()

Just like the slice array in native JS, this array contains two arguments, start and end. Start represents the start position and end represents the end position, excluding the end element. If end is not included, the whole object is returned; if end is negative, the corresponding data (start, length-end) is returned. If start has only one and is negative, the last end element is returned.

Usage:

Immutable.fromJS([1, 2, 3, 4]).slice(0); / / [1, 2, 3, 4] Immutable. FromJS ([1, 2, 3, 4]). The slice (0, 2); / / [1, 2] Immutable. FromJS ([1, 2, 3, 4]). The slice (2); / / [3, 4] Immutable. FromJS ([1, 2, 3, 4]). The slice (0, 2); / / [1, 2]Copy the code
rest()

Function: Returns all elements except the first one

Usage:

Immutable. FromJS ([1, 2, 3, 4]). The rest () / / (2 and 4)Copy the code
butLast()

Function: Returns all elements except the last one

Usage:

Immutable. FromJS ([1, 2, 3, 4]). The rest () / / [1, 2, 3]Copy the code
skip()

Action: takes an argument n and returns all elements left after the first n elements are truncated

Usage:

Immutable. FromJS ([1, 2, 3, 4]). Skip (1) / / \ [2 4]Copy the code
skipLast()

Action: Takes an argument n and returns all elements left after the last n elements are truncated

Usage:

Immutable. FromJS ([1, 2, 3, 4]). Skip (1) / / [1, 2, 3]Copy the code
skipWhile()

Function: Returns all elements since the first return false

Immutable.fromJS([1, 2, 3, 4]).skipWhile(list.skipWhile((value,index,list)=>{ return value > 2; })) / / [1, 2, 3, 4] skipUntil ()Copy the code

Function: Returns all elements since the first return true

Immutable.fromJS([1, 2, 3, 4]).skipUntil(list.skipWhile((value,index,list)=>{ return value > 2; })) / / [3, 4]Copy the code
take()

Action: Returns the first n elements with an argument n

Usage:

Immutable. FromJS ([1, 2, 3, 4]), take (2) / / [1, 2]Copy the code
takeLast()

Action: Returns the last n elements with an argument n

Usage:

Immutable. FromJS ([1, 2, 3, 4]). TakeLast (2) / / [3, 4]Copy the code
takeWhile()

Function: Returns all elements from before the first return false

Immutable.fromJS([1, 2, 3, 4]).skipWhile(list.takeWhile((value,index,list)=>{ return value > 2; })) / / []Copy the code
takeUntil()

Function: Returns all elements before the first return true

Immutable.fromJS([1, 2, 3, 4]).skipUntil(list.takeUntil((value,index,list)=>{ return value > 2; })) / / [1, 2]Copy the code

Process the data

reduce()

Function: In the same way as reduce in js arrays, elements are processed in ascending order by index

Usage:

Immutable. FromJS ([1, 2, 3, 4]) reduce ((pre, next, index, arr) = > {the console. The log (pre + next) return the pre + next; }) // 3 6 10Copy the code
reduceRight()

Function: In the same way as reduce in js arrays, elements are processed in descending order by index

Usage:

Immutable. FromJS ([1, 2, 3, 4]) reduceRight ((pre, next, index, arr) = > {the console. The log (pre + next) return the pre + next; }) // 7 9 10Copy the code
every()

Check whether all elements in the whole object meet a certain condition, return true, otherwise return false.

Code:

Every ((value,index,arr)=>{return value > 2}) // falseCopy the code
some()

Function: Check whether all elements in the whole object meet a certain condition, if there is an element, return true, otherwise return false.

Code:

Immutable.fromjs ([1,2,3,4]).some((value,index,arr)=>{return value > 2}) // trueCopy the code
join()

Function: Joins an array in js. Change the quasi to a string

Usage:

Immutable. FromJS ([1, 2, 3, 4]). The join (', ') / / 1, 2, 3, 4Copy the code
isEmpty()

Function: Check whether it is empty

Usage:

Immutable.fromJS([]).isEmpty(); //true
Immutable.fromJS({}).isEmpty(); //true
Copy the code
count()

Function: Return the number of elements, can be customized conditions, return the number of conditions

Usage:

Constlist = Immutable. FromJS ([1, 2, 3, 4]); constmap = Immutable.fromJS({a:1,b:2,c:3,d:4}); list.count((value,index,list)=>{ return value > 2; }) //2map.count((value,index,list)=>{ return value > 2; / / 2})Copy the code
countBy()

What it does: Unlike count, countBy returns an object

Usage:

Constlist = Immutable. FromJS ([1, 2, 3, 4]); constmap = Immutable.fromJS({a:1,b:2,c:3,d:4}); list.countBy((value,index,list)=>{ return value > 2; } //{false: 2, true: 2}map.countBy((value,index,list)=>{ return value > 2; } //{false: 2, true: 2}Copy the code

reference

Immutable -js Official document