Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Immutable persistent data collections for Javascript which increase efficiency and simplicity.

License

NotificationsYou must be signed in to change notification settings

immutable-js/immutable-js

Repository files navigation

Build StatusChat on slack

Read the docs and eat your vegetables.

Docs are automatically generated fromREADME.md andimmutable.d.ts.Please contribute! Also, don't miss thewiki which contains articles onadditional specific topics. Can't find something? Open anissue.

Table of contents:

Introduction

Immutable data cannot be changed once created, leading to much simplerapplication development, no defensive copying, and enabling advanced memoizationand change detection techniques with simple logic.Persistent data presentsa mutative API which does not update the data in-place, but instead alwaysyields new updated data.

Immutable.js provides many Persistent Immutable data structures including:List,Stack,Map,OrderedMap,Set,OrderedSet andRecord.

These data structures are highly efficient on modern JavaScript VMs by usingstructural sharing viahash maps tries andvector tries as popularizedby Clojure and Scala, minimizing the need to copy or cache data.

Immutable.js also provides a lazySeq, allowing efficientchaining of collection methods likemap andfilter without creatingintermediate representations. Create someSeq withRange andRepeat.

Want to hear more? Watch the presentation about Immutable.js:

Immutable Data and React

Getting started

Installimmutable using npm.

# using npmnpm install immutable# using Yarnyarn add immutable# using pnpmpnpm add immutable# using Bunbun add immutable

Then require it into any module.

import{Map}from'immutable';constmap1=Map({a:1,b:2,c:3});constmap2=map1.set('b',50);map1.get('b')+' vs. '+map2.get('b');// 2 vs. 50

Browser

Immutable.js has no dependencies, which makes it predictable to include in a Browser.

It's highly recommended to use a module bundler likewebpack,rollup, orbrowserify. Theimmutable npm module workswithout any additional consideration. All examples throughout the documentationwill assume use of this kind of tool.

Alternatively, Immutable.js may be directly included as a script tag. Downloador link to a CDN such asCDNJSorjsDelivr.

Use a script tag to directly addImmutable to the global scope:

<scriptsrc="immutable.min.js"></script><script>varmap1=Immutable.Map({a:1,b:2,c:3});varmap2=map1.set('b',50);map1.get('b');// 2map2.get('b');// 50</script>

Or use an AMD-style loader (such asRequireJS):

require(['./immutable.min.js'],function(Immutable){varmap1=Immutable.Map({a:1,b:2,c:3});varmap2=map1.set('b',50);map1.get('b');// 2map2.get('b');// 50});

Flow & TypeScript

Use these Immutable collections and sequences as you would use nativecollections in yourFlowtype orTypeScript programs while still takingadvantage of type generics, error detection, and auto-complete in your IDE.

Installingimmutable via npm brings with it type definitions for Flow (v0.55.0 or higher)and TypeScript (v4.5 or higher), so you shouldn't need to do anything at all!

Using TypeScript with Immutable.js v4+

Immutable.js type definitions embrace ES2015. While Immutable.js itself supportslegacy browsers and environments, its type definitions require TypeScript's 2015lib. Include either"target": "es2015" or"lib": "es2015" in yourtsconfig.json, or provide--target es2015 or--lib es2015 to thetsc command.

import{Map}from'immutable';constmap1=Map({a:1,b:2,c:3});constmap2=map1.set('b',50);map1.get('b')+' vs. '+map2.get('b');// 2 vs. 50

Using TypeScript with Immutable.js v3 and earlier:

Previous versions of Immutable.js include a reference file which you can includevia relative path to the type definitions at the top of your file.

///<reference path='./node_modules/immutable/dist/immutable.d.ts'/>import{Map}from'immutable';varmap1:Map<string,number>;map1=Map({a:1,b:2,c:3});varmap2=map1.set('b',50);map1.get('b');// 2map2.get('b');// 50

The case for Immutability

Much of what makes application development difficult is tracking mutation andmaintaining state. Developing with immutable data encourages you to thinkdifferently about how data flows through your application.

Subscribing to data events throughout your application creates a huge overhead ofbook-keeping which can hurt performance, sometimes dramatically, and createsopportunities for areas of your application to get out of sync with each otherdue to easy to make programmer error. Since immutable data never changes,subscribing to changes throughout the model is a dead-end and new data can onlyever be passed from above.

This model of data flow aligns well with the architecture ofReactand especially well with an application designed using the ideas ofFlux.

When data is passed from above rather than being subscribed to, and you're onlyinterested in doing work when something has changed, you can use equality.

Immutable collections should be treated asvalues rather thanobjects. Whileobjects represent some thing which could change over time, a value representsthe state of that thing at a particular instance of time. This principle is mostimportant to understanding the appropriate use of immutable data. In order totreat Immutable.js collections as values, it's important to use theImmutable.is() function or.equals() method to determinevalue equalityinstead of the=== operator which determines objectreference identity.

import{Map}from'immutable';constmap1=Map({a:1,b:2,c:3});constmap2=Map({a:1,b:2,c:3});map1.equals(map2);// truemap1===map2;// false

Note: As a performance optimization Immutable.js attempts to return the existingcollection when an operation would result in an identical collection, allowingfor using=== reference equality to determine if something definitely has notchanged. This can be extremely useful when used within a memoization functionwhich would prefer to re-run the function if a deeper equality check couldpotentially be more costly. The=== equality check is also used internally byImmutable.is and.equals() as a performance optimization.

import{Map}from'immutable';constmap1=Map({a:1,b:2,c:3});constmap2=map1.set('b',2);// Set to same valuemap1===map2;// true

If an object is immutable, it can be "copied" simply by making another referenceto it instead of copying the entire object. Because a reference is much smallerthan the object itself, this results in memory savings and a potential boost inexecution speed for programs which rely on copies (such as an undo-stack).

import{Map}from'immutable';constmap=Map({a:1,b:2,c:3});constmapCopy=map;// Look, "copies" are free!

JavaScript-first API

While Immutable.js is inspired by Clojure, Scala, Haskell and other functionalprogramming environments, it's designed to bring these powerful concepts toJavaScript, and therefore has an Object-Oriented API that closely mirrors thatofES2015Array,Map, andSet.

The difference for the immutable collections is that methods which would mutatethe collection, likepush,set,unshift orsplice, instead return a newimmutable collection. Methods which return new arrays, likeslice orconcat,instead return new immutable collections.

import{List}from'immutable';constlist1=List([1,2]);constlist2=list1.push(3,4,5);constlist3=list2.unshift(0);constlist4=list1.concat(list2,list3);assert.equal(list1.size,2);assert.equal(list2.size,5);assert.equal(list3.size,6);assert.equal(list4.size,13);assert.equal(list4.get(0),1);

Almost all of the methods onArray will be found in similar form onImmutable.List, those ofMap found onImmutable.Map, and those ofSetfound onImmutable.Set, including collection operations likeforEach()andmap().

import{Map}from'immutable';constalpha=Map({a:1,b:2,c:3,d:4});alpha.map((v,k)=>k.toUpperCase()).join();// 'A,B,C,D'

Convert from raw JavaScript objects and arrays.

Designed to inter-operate with your existing JavaScript, Immutable.jsaccepts plain JavaScript Arrays and Objects anywhere a method expects aCollection.

import{Map,List}from'immutable';constmap1=Map({a:1,b:2,c:3,d:4});constmap2=Map({c:10,a:20,t:30});constobj={d:100,o:200,g:300};constmap3=map1.merge(map2,obj);// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }constlist1=List([1,2,3]);constlist2=List([4,5,6]);constarray=[7,8,9];constlist3=list1.concat(list2,array);// List [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

This is possible because Immutable.js can treat any JavaScript Array or Objectas a Collection. You can take advantage of this in order to get sophisticatedcollection methods on JavaScript Objects, which otherwise have a very sparsenative API. Because Seq evaluates lazily and does not cache intermediateresults, these operations can be extremely efficient.

import{Seq}from'immutable';constmyObject={a:1,b:2,c:3};Seq(myObject).map((x)=>x*x).toObject();// { a: 1, b: 4, c: 9 }

Keep in mind, when using JS objects to construct Immutable Maps, thatJavaScript Object properties are always strings, even if written in a quote-lessshorthand, while Immutable Maps accept keys of any type.

import{fromJS}from'immutable';constobj={1:'one'};console.log(Object.keys(obj));// [ "1" ]console.log(obj['1'],obj[1]);// "one", "one"constmap=fromJS(obj);console.log(map.get('1'),map.get(1));// "one", undefined

Property access for JavaScript Objects first converts the key to a string, butsince Immutable Map keys can be of any type the argument toget() isnot altered.

Converts back to raw JavaScript objects.

All Immutable.js Collections can be converted to plain JavaScript Arrays andObjects shallowly withtoArray() andtoObject() or deeply withtoJS().All Immutable Collections also implementtoJSON() allowing them to be passedtoJSON.stringify directly. They also respect the customtoJSON() methods ofnested objects.

import{Map,List}from'immutable';constdeep=Map({a:1,b:2,c:List([3,4,5])});console.log(deep.toObject());// { a: 1, b: 2, c: List [ 3, 4, 5 ] }console.log(deep.toArray());// [ 1, 2, List [ 3, 4, 5 ] ]console.log(deep.toJS());// { a: 1, b: 2, c: [ 3, 4, 5 ] }JSON.stringify(deep);// '{"a":1,"b":2,"c":[3,4,5]}'

Embraces ES2015

Immutable.js supports all JavaScript environments, including legacybrowsers (even IE11). However it also takes advantage of features added toJavaScript inES2015, the latest standard version of JavaScript, includingIterators,Arrow Functions,Classes, andModules. It's inspiredby the nativeMap andSet collections added to ES2015.

All examples in the Documentation are presented in ES2015. To run in allbrowsers, they need to be translated to ES5.

// ES2015constmapped=foo.map((x)=>x*x);// ES5varmapped=foo.map(function(x){returnx*x;});

All Immutable.js collections areIterable, which allows them to beused anywhere an Iterable is expected, such as when spreading into an Array.

import{List}from'immutable';constaList=List([1,2,3]);constanArray=[0, ...aList,4,5];// [ 0, 1, 2, 3, 4, 5 ]

Note: A Collection is always iterated in the same order, however that order maynot always be well defined, as is the case for theMap andSet.

Nested Structures

The collections in Immutable.js are intended to be nested, allowing for deeptrees of data, similar to JSON.

import{fromJS}from'immutable';constnested=fromJS({a:{b:{c:[3,4,5]}}});// Map { a: Map { b: Map { c: List [ 3, 4, 5 ] }}}

A few power-tools allow for reading and operating on nested data. Themost useful aremergeDeep,getIn,setIn, andupdateIn, found onList,Map andOrderedMap.

import{fromJS}from'immutable';constnested=fromJS({a:{b:{c:[3,4,5]}}});constnested2=nested.mergeDeep({a:{b:{d:6}}});// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 }}}console.log(nested2.getIn(['a','b','d']));// 6constnested3=nested2.updateIn(['a','b','d'],(value)=>value+1);console.log(nested3);// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 }}}constnested4=nested3.updateIn(['a','b','c'],(list)=>list.push(6));// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 }}}

Equality treats Collections as Values

Immutable.js collections are treated as pure datavalues. Two immutablecollections are consideredvalue equal (via.equals() oris()) if theyrepresent the same collection of values. This differs from JavaScript's typicalreference equal (via=== or==) for Objects and Arrays which onlydetermines if two variables represent references to the same object instance.

Consider the example below where two identicalMap instances are notreference equal but arevalue equal.

// First consider:constobj1={a:1,b:2,c:3};constobj2={a:1,b:2,c:3};obj1!==obj2;// two different instances are always not equal with ===import{Map,is}from'immutable';constmap1=Map({a:1,b:2,c:3});constmap2=Map({a:1,b:2,c:3});map1!==map2;// two different instances are not reference-equalmap1.equals(map2);// but are value-equal if they have the same valuesis(map1,map2);// alternatively can use the is() function

Value equality allows Immutable.js collections to be used as keys in Maps orvalues in Sets, and retrieved with different but equivalent collections:

import{Map,Set}from'immutable';constmap1=Map({a:1,b:2,c:3});constmap2=Map({a:1,b:2,c:3});constset=Set().add(map1);set.has(map2);// true because these are value-equal

Note:is() uses the same measure of equality asObject.is for scalarstrings and numbers, but uses value equality for Immutable collections,determining if both are immutable and all keys and values are equalusing the same measure of equality.

Performance tradeoffs

While value equality is useful in many circumstances, it has differentperformance characteristics than reference equality. Understanding thesetradeoffs may help you decide which to use in each case, especially when usedto memoize some operation.

When comparing two collections, value equality may require considering everyitem in each collection, on anO(N) time complexity. For large collections ofvalues, this could become a costly operation. Though if the two are not equaland hardly similar, the inequality is determined very quickly. In contrast, whencomparing two collections with reference equality, only the initial referencesto memory need to be compared which is not based on the size of the collections,which has anO(1) time complexity. Checking reference equality is always veryfast, however just because two collections are not reference-equal does not ruleout the possibility that they may be value-equal.

Return self on no-op optimization

When possible, Immutable.js avoids creating new objects for updates where nochange invalue occurred, to allow for efficientreference equality checkingto quickly determine if no change occurred.

import{Map}from'immutable';constoriginalMap=Map({a:1,b:2,c:3});constupdatedMap=originalMap.set('b',2);updatedMap===originalMap;// No-op .set() returned the original reference.

However updates which do result in a change will return a new reference. Eachof these operations occur independently, so two similar updates will not returnthe same reference:

import{Map}from'immutable';constoriginalMap=Map({a:1,b:2,c:3});constupdatedMap=originalMap.set('b',1000);// New instance, leaving the original immutable.updatedMap!==originalMap;constanotherUpdatedMap=originalMap.set('b',1000);// Despite both the results of the same operation, each created a new reference.anotherUpdatedMap!==updatedMap;// However the two are value equal.anotherUpdatedMap.equals(updatedMap);

Batching Mutations

If a tree falls in the woods, does it make a sound?

If a pure function mutates some local data in order to produce an immutablereturn value, is that ok?

— Rich Hickey, Clojure

Applying a mutation to create a new immutable object results in some overhead,which can add up to a minor performance penalty. If you need to apply a seriesof mutations locally before returning, Immutable.js gives you the ability tocreate a temporary mutable (transient) copy of a collection and apply a batch ofmutations in a performant manner by usingwithMutations. In fact, this isexactly how Immutable.js applies complex mutations itself.

As an example, buildinglist2 results in the creation of 1, not 3, newimmutable Lists.

import{List}from'immutable';constlist1=List([1,2,3]);constlist2=list1.withMutations(function(list){list.push(4).push(5).push(6);});assert.equal(list1.size,3);assert.equal(list2.size,6);

Note: Immutable.js also providesasMutable andasImmutable, but onlyencourages their use whenwithMutations will not suffice. Use caution to notreturn a mutable copy, which could result in undesired behavior.

Important!: Only a select few methods can be used inwithMutations includingset,push andpop. These methods can be applied directly against apersistent data-structure where other methods likemap,filter,sort,andsplice will always return new immutable data-structures and never mutatea mutable collection.

Lazy Seq

Seq describes a lazy operation, allowing them to efficiently chainuse of all the higher-order collection methods (such asmap andfilter)by not creating intermediate collections.

Seq is immutable — Once a Seq is created, it cannot bechanged, appended to, rearranged or otherwise modified. Instead, any mutativemethod called on aSeq will return a newSeq.

Seq is lazySeq does as little work as necessary to respond to anymethod call. Values are often created during iteration, including implicititeration when reducing or converting to a concrete data structure such asaList or JavaScriptArray.

For example, the following performs no work, because the resultingSeq's values are never iterated:

import{Seq}from'immutable';constoddSquares=Seq([1,2,3,4,5,6,7,8]).filter((x)=>x%2!==0).map((x)=>x*x);

Once theSeq is used, it performs only the work necessary. In thisexample, no intermediate arrays are ever created, filter is called threetimes, and map is only called once:

oddSquares.get(1);// 9

Any collection can be converted to a lazy Seq withSeq().

import{Map,Seq}from'immutable';constmap=Map({a:1,b:2,c:3});constlazySeq=Seq(map);

Seq allows for the efficient chaining of operations, allowing for theexpression of logic that can otherwise be very tedious:

lazySeq.flip().map((key)=>key.toUpperCase()).flip();// Seq { A: 1, B: 2, C: 3 }

As well as expressing logic that would otherwise seem memory or timelimited, for exampleRange is a special kind of Lazy sequence.

import{Range}from'immutable';Range(1,Infinity).skip(1000).map((n)=>-n).filter((n)=>n%2===0).take(2).reduce((r,n)=>r*n,1);// 1006008

Comparison of filter(), groupBy(), and partition()

Thefilter(),groupBy(), andpartition() methods are similar in that theyall divide a collection into parts based on applying a function to each element.All three call the predicate or grouping function once for each item in theinput collection. All three return zero or more collections of the same type astheir input. The returned collections are always distinct from the input(according to===), even if the contents are identical.

Of these methods,filter() is the only one that is lazy and the only one whichdiscards items from the input collection. It is the simplest to use, and thefact that it returns exactly one collection makes it easy to combine with othermethods to form a pipeline of operations.

Thepartition() method is similar to an eager version offilter(), but itreturns two collections; the first contains the items that would have beendiscarded byfilter(), and the second contains the items that would have beenkept. It always returns an array of exactly two collections, which can make iteasier to use thangroupBy(). Compared to making two separate calls tofilter(),partition() makes half as many calls it the predicate passed toit.

ThegroupBy() method is a more generalized version ofpartition() that cangroup by an arbitrary function rather than just a predicate. It returns a mapwith zero or more entries, where the keys are the values returned by thegrouping function, and the values are nonempty collections of the correspondingarguments. AlthoughgroupBy() is more powerful thanpartition(), it can beharder to use because it is not always possible predict in advance how manyentries the returned map will have and what their keys will be.

SummaryfilterpartitiongroupBy
ease of useeasiestmoderatehardest
generalityleastmoderatemost
lazinesslazyeagereager
# of returned sub-collections120 or more
sub-collections may be emptyyesyesno
can discard itemsyesnono
wrapping containernonearrayMap/OrderedMap

Additional Tools and Resources

  • Atom-store

    • A Clojure-inspired atom implementation in Javascript with configurabilityfor external persistance.
  • Chai Immutable

    • If you are using theChai Assertion Library, thisprovides a set of assertions to use against Immutable.js collections.
  • Fantasy-land

    • Specification for interoperability of common algebraic structures in JavaScript.
  • Immutagen

    • A library for simulating immutable generators in JavaScript.
  • Immutable-cursor

    • Immutable cursors incorporating the Immutable.js interface overClojure-inspired atom.
  • Immutable-ext

    • Fantasyland extensions for immutablejs
  • Immutable-js-tools

    • Util tools for immutable.js
  • Immutable-Redux

    • redux-immutable is used to create an equivalent function of ReduxcombineReducers that works with Immutable.js state.
  • Immutable-Treeutils

    • Functional tree traversal helpers for ImmutableJS data structures.
  • Irecord

    • An immutable store that exposes an RxJS observable. Great for React.
  • Mudash

    • Lodash wrapper providing Immutable.JS support.
  • React-Immutable-PropTypes

    • PropType validators that work with Immutable.js.
  • Redux-Immutablejs

    • Redux Immutable facilities.
  • Rxstate

    • Simple opinionated state management library based on RxJS and Immutable.js.
  • Transit-Immutable-js

    • Transit serialisation for Immutable.js.
    • See also:Transit-js

Have an additional tool designed to work with Immutable.js?Submit a PR to add it to this list in alphabetical order.

Contributing

UseGithub issues for requests.

We actively welcome pull requests, learn how tocontribute.

Immutable.js is maintained within theContributor Covenant's Code of Conduct.

Changelog

Changes are tracked asGithub releases.

License

Immutable.js isMIT-licensed.

Thanks

Phil Bagwell, for his inspirationand research in persistent data structures.

Hugh Jackson, for providing the npm packagename. If you're looking for his unsupported package, seethis repository.

About

Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp