GithubHelp home page GithubHelp logo

immutable-js / immutable-js Goto Github PK

View Code? Open in Web Editor NEW
32.9K 509.0 1.8K 59.83 MB

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

Home Page: https://immutable-js.com

License: MIT License

JavaScript 40.18% TypeScript 58.27% Shell 0.18% CSS 1.38%

immutable-js's Introduction

Immutable collections for JavaScript

Build Status Chat on slack

Read the docs and eat your vegetables.

Docs are automatically generated from README.md and immutable.d.ts. Please contribute! Also, don't miss the wiki which contains articles on additional specific topics. Can't find something? Open an issue.

Table of contents:

Introduction

Immutable data cannot be changed once created, leading to much simpler application development, no defensive copying, and enabling advanced memoization and change detection techniques with simple logic. Persistent data presents a mutative API which does not update the data in-place, but instead always yields new updated data.

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

These data structures are highly efficient on modern JavaScript VMs by using structural sharing via hash maps tries and vector tries as popularized by Clojure and Scala, minimizing the need to copy or cache data.

Immutable.js also provides a lazy Seq, allowing efficient chaining of collection methods like map and filter without creating intermediate representations. Create some Seq with Range and Repeat.

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

Immutable Data and React

Getting started

Install immutable using npm.

# using npm
npm install immutable

# using Yarn
yarn add immutable

# using pnpm
pnpm add immutable

# using Bun
bun add immutable

Then require it into any module.

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = 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 like webpack, rollup, or browserify. The immutable npm module works without any additional consideration. All examples throughout the documentation will assume use of this kind of tool.

Alternatively, Immutable.js may be directly included as a script tag. Download or link to a CDN such as CDNJS or jsDelivr.

Use a script tag to directly add Immutable to the global scope:

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

Or use an AMD-style loader (such as RequireJS):

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

Flow & TypeScript

Use these Immutable collections and sequences as you would use native collections in your Flowtype or TypeScript programs while still taking advantage of type generics, error detection, and auto-complete in your IDE.

Installing immutable via npm brings with it type definitions for Flow (v0.55.0 or higher) and TypeScript (v2.1.0 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 supports legacy browsers and environments, its type definitions require TypeScript's 2015 lib. Include either "target": "es2015" or "lib": "es2015" in your tsconfig.json, or provide --target es2015 or --lib es2015 to the tsc command.

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = 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 include via relative path to the type definitions at the top of your file.

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

The case for Immutability

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

Subscribing to data events throughout your application creates a huge overhead of book-keeping which can hurt performance, sometimes dramatically, and creates opportunities for areas of your application to get out of sync with each other due 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 only ever be passed from above.

This model of data flow aligns well with the architecture of React and especially well with an application designed using the ideas of Flux.

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

Immutable collections should be treated as values rather than objects. While objects represent some thing which could change over time, a value represents the state of that thing at a particular instance of time. This principle is most important to understanding the appropriate use of immutable data. In order to treat Immutable.js collections as values, it's important to use the Immutable.is() function or .equals() method to determine value equality instead of the === operator which determines object reference identity.

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = Map({ a: 1, b: 2, c: 3 });
map1.equals(map2); // true
map1 === map2; // false

Note: As a performance optimization Immutable.js attempts to return the existing collection when an operation would result in an identical collection, allowing for using === reference equality to determine if something definitely has not changed. This can be extremely useful when used within a memoization function which would prefer to re-run the function if a deeper equality check could potentially be more costly. The === equality check is also used internally by Immutable.is and .equals() as a performance optimization.

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 2); // Set to same value
map1 === map2; // true

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

const { Map } = require('immutable');
const map = Map({ a: 1, b: 2, c: 3 });
const mapCopy = map; // Look, "copies" are free!

JavaScript-first API

While Immutable.js is inspired by Clojure, Scala, Haskell and other functional programming environments, it's designed to bring these powerful concepts to JavaScript, and therefore has an Object-Oriented API that closely mirrors that of ES2015 Array, Map, and Set.

The difference for the immutable collections is that methods which would mutate the collection, like push, set, unshift or splice, instead return a new immutable collection. Methods which return new arrays, like slice or concat, instead return new immutable collections.

const { List } = require('immutable');
const list1 = List([1, 2]);
const list2 = list1.push(3, 4, 5);
const list3 = list2.unshift(0);
const list4 = 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 on Array will be found in similar form on Immutable.List, those of Map found on Immutable.Map, and those of Set found on Immutable.Set, including collection operations like forEach() and map().

const { Map } = require('immutable');
const alpha = 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.js accepts plain JavaScript Arrays and Objects anywhere a method expects a Collection.

const { Map, List } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3, d: 4 });
const map2 = Map({ c: 10, a: 20, t: 30 });
const obj = { d: 100, o: 200, g: 300 };
const map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }
const list1 = List([1, 2, 3]);
const list2 = List([4, 5, 6]);
const array = [7, 8, 9];
const list3 = 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 Object as a Collection. You can take advantage of this in order to get sophisticated collection methods on JavaScript Objects, which otherwise have a very sparse native API. Because Seq evaluates lazily and does not cache intermediate results, these operations can be extremely efficient.

const { Seq } = require('immutable');
const myObject = { 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, that JavaScript Object properties are always strings, even if written in a quote-less shorthand, while Immutable Maps accept keys of any type.

const { fromJS } = require('immutable');

const obj = { 1: 'one' };
console.log(Object.keys(obj)); // [ "1" ]
console.log(obj['1'], obj[1]); // "one", "one"

const map = fromJS(obj);
console.log(map.get('1'), map.get(1)); // "one", undefined

Property access for JavaScript Objects first converts the key to a string, but since Immutable Map keys can be of any type the argument to get() is not altered.

Converts back to raw JavaScript objects.

All Immutable.js Collections can be converted to plain JavaScript Arrays and Objects shallowly with toArray() and toObject() or deeply with toJS(). All Immutable Collections also implement toJSON() allowing them to be passed to JSON.stringify directly. They also respect the custom toJSON() methods of nested objects.

const { Map, List } = require('immutable');
const deep = 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 legacy browsers (even IE11). However it also takes advantage of features added to JavaScript in ES2015, the latest standard version of JavaScript, including Iterators, Arrow Functions, Classes, and Modules. It's inspired by the native Map and Set collections added to ES2015.

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

// ES2015
const mapped = foo.map(x => x * x);
// ES5
var mapped = foo.map(function (x) {
  return x * x;
});

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

const { List } = require('immutable');
const aList = List([1, 2, 3]);
const anArray = [0, ...aList, 4, 5]; // [ 0, 1, 2, 3, 4, 5 ]

Note: A Collection is always iterated in the same order, however that order may not always be well defined, as is the case for the Map and Set.

Nested Structures

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

const { fromJS } = require('immutable');
const nested = 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. The most useful are mergeDeep, getIn, setIn, and updateIn, found on List, Map and OrderedMap.

const { fromJS } = require('immutable');
const nested = fromJS({ a: { b: { c: [3, 4, 5] } } });

const nested2 = 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'])); // 6

const nested3 = nested2.updateIn(['a', 'b', 'd'], value => value + 1);
console.log(nested3);
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }

const nested4 = 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 data values. Two immutable collections are considered value equal (via .equals() or is()) if they represent the same collection of values. This differs from JavaScript's typical reference equal (via === or ==) for Objects and Arrays which only determines if two variables represent references to the same object instance.

Consider the example below where two identical Map instances are not reference equal but are value equal.

// First consider:
const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { a: 1, b: 2, c: 3 };
obj1 !== obj2; // two different instances are always not equal with ===

const { Map, is } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = Map({ a: 1, b: 2, c: 3 });
map1 !== map2; // two different instances are not reference-equal
map1.equals(map2); // but are value-equal if they have the same values
is(map1, map2); // alternatively can use the is() function

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

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

Note: is() uses the same measure of equality as Object.is for scalar strings and numbers, but uses value equality for Immutable collections, determining if both are immutable and all keys and values are equal using the same measure of equality.

Performance tradeoffs

While value equality is useful in many circumstances, it has different performance characteristics than reference equality. Understanding these tradeoffs may help you decide which to use in each case, especially when used to memoize some operation.

When comparing two collections, value equality may require considering every item in each collection, on an O(N) time complexity. For large collections of values, this could become a costly operation. Though if the two are not equal and hardly similar, the inequality is determined very quickly. In contrast, when comparing two collections with reference equality, only the initial references to memory need to be compared which is not based on the size of the collections, which has an O(1) time complexity. Checking reference equality is always very fast, however just because two collections are not reference-equal does not rule out 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 no change in value occurred, to allow for efficient reference equality checking to quickly determine if no change occurred.

const { Map } = require('immutable');
const originalMap = Map({ a: 1, b: 2, c: 3 });
const updatedMap = 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. Each of these operations occur independently, so two similar updates will not return the same reference:

const { Map } = require('immutable');
const originalMap = Map({ a: 1, b: 2, c: 3 });
const updatedMap = originalMap.set('b', 1000);
// New instance, leaving the original immutable.
updatedMap !== originalMap;
const anotherUpdatedMap = 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 immutable return 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 series of mutations locally before returning, Immutable.js gives you the ability to create a temporary mutable (transient) copy of a collection and apply a batch of mutations in a performant manner by using withMutations. In fact, this is exactly how Immutable.js applies complex mutations itself.

As an example, building list2 results in the creation of 1, not 3, new immutable Lists.

const { List } = require('immutable');
const list1 = List([1, 2, 3]);
const list2 = 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 provides asMutable and asImmutable, but only encourages their use when withMutations will not suffice. Use caution to not return a mutable copy, which could result in undesired behavior.

Important!: Only a select few methods can be used in withMutations including set, push and pop. These methods can be applied directly against a persistent data-structure where other methods like map, filter, sort, and splice will always return new immutable data-structures and never mutate a mutable collection.

Lazy Seq

Seq describes a lazy operation, allowing them to efficiently chain use of all the higher-order collection methods (such as map and filter) by not creating intermediate collections.

Seq is immutable โ€” Once a Seq is created, it cannot be changed, appended to, rearranged or otherwise modified. Instead, any mutative method called on a Seq will return a new Seq.

Seq is lazy โ€” Seq does as little work as necessary to respond to any method call. Values are often created during iteration, including implicit iteration when reducing or converting to a concrete data structure such as a List or JavaScript Array.

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

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

Once the Seq is used, it performs only the work necessary. In this example, no intermediate arrays are ever created, filter is called three times, and map is only called once:

oddSquares.get(1); // 9

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

const { Map, Seq } = require('immutable');
const map = Map({ a: 1, b: 2, c: 3 });
const lazySeq = Seq(map);

Seq allows for the efficient chaining of operations, allowing for the expression 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 time limited, for example Range is a special kind of Lazy sequence.

const { Range } = require('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()

The filter(), groupBy(), and partition() methods are similar in that they all 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 the input collection. All three return zero or more collections of the same type as their 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 which discards items from the input collection. It is the simplest to use, and the fact that it returns exactly one collection makes it easy to combine with other methods to form a pipeline of operations.

The partition() method is similar to an eager version of filter(), but it returns two collections; the first contains the items that would have been discarded by filter(), and the second contains the items that would have been kept. It always returns an array of exactly two collections, which can make it easier to use than groupBy(). Compared to making two separate calls to filter(), partition() makes half as many calls it the predicate passed to it.

The groupBy() method is a more generalized version of partition() that can group by an arbitrary function rather than just a predicate. It returns a map with zero or more entries, where the keys are the values returned by the grouping function, and the values are nonempty collections of the corresponding arguments. Although groupBy() is more powerful than partition(), it can be harder to use because it is not always possible predict in advance how many entries the returned map will have and what their keys will be.

Summary filter partition groupBy
ease of use easiest moderate hardest
generality least moderate most
laziness lazy eager eager
# of returned sub-collections 1 2 0 or more
sub-collections may be empty yes yes no
can discard items yes no no
wrapping container none array Map/OrderedMap

Additional Tools and Resources

  • Atom-store

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

    • If you are using the Chai Assertion Library, this provides 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 over Clojure-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 Redux combineReducers 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

Use Github issues for requests.

We actively welcome pull requests, learn how to contribute.

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

Changelog

Changes are tracked as Github releases.

License

Immutable.js is MIT-licensed.

Thanks

Phil Bagwell, for his inspiration and research in persistent data structures.

Hugh Jackson, for providing the npm package name. If you're looking for his unsupported package, see this repository.

immutable-js's People

Contributors

abacaphiliac avatar acusti avatar alex35mil avatar avocadowastaken avatar bdurrer avatar comatory avatar conartist6 avatar dashed avatar dsissitka-private avatar ellbee avatar flarnie avatar hoorayimhelping avatar howardjing avatar jdeniau avatar leebyron avatar marudor avatar matthewwithanm avatar mcclure avatar methuselah96 avatar philipp-spiess avatar pluma avatar remmycat avatar rishav-jha-mech avatar sophiebits avatar strate avatar theo-matzavinos avatar vinnymac avatar wmertens avatar wokalski avatar zertosh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

immutable-js's Issues

Produce a diff from immutable objects?

I would like to use immutable-js as diff for app state in the same manner as React diffs DOM state. Is this even posible? Can you direct me to the proper/better approach? I would like to store app state diff into localStorage and sync with server. Thank you for the awesome library.

Browser pretty printing

Browsers (Chrome, Safari and Firefox) don't pretty print immutable objects when logging them.

For example:

console.log(Immutable.Vector(1, 2, 3, 4, 5));
// => [undefined x 5]

I imagine this may prove rather inconvenient as a structure becomes more complex.

Writing a custom logger function like this fixes my issue:

function myLogger() {
  var strings = Array.prototype.slice.call(arguments).map(function (a) { 
    return a.toString();
  });
  console.log.apply(console, strings);
}

myLogger(Immutable.Vector(1, 2, 3, 4, 5));
// => Vector [ 1, 2, 3, 4, 5 ]

incorrect behavior for Vector.slice/splice

The library has overall been great to work with but i hit trouble when trying to replace a specific item within a sequence (i have a list of immutable maps and one of the contained maps has changes and needs to be replaced in the list). Nothing quite seems to work, however:

> var Immutable = require('immutable');
undefined
> v = Immutable.Vector(1,2,3);
Vector [ 1, 2, 3 ]
> v2 = Immutable.Vector.from(v.splice(0,1,0))
Vector [ , ,  ]

v.splice() works on its own but the conversion back to vector fails.

> Immutable.Vector.from(v.slice(0,0).concat(v.slice(1)))
Vector [ ,  ]

again, works on its own but conversion back to vector fails

> v.slice(0,0).concat(v.slice(1))
Seq [ , , , 2, 3 ]
> Immutable.Vector.from(v.slice(0,0).concat(v.slice(1)))
Vector [ ,  ]
> Immutable.Vector(v.slice(0,0).concat(v.slice(1)))
Vector [ Seq [ , , , 2, 3 ] ]

same here.

Bug in handling collisions in Map

I have noticed that when key with different hash is set to HashCollisionNode new BitmapIndexedNode is being created with wrong hashValue for HashCollisionNode (hash value of new key is used instead of collisionHash).

This piece of code leads to "Maximum call stack size exceeded"

var map = Immutable.Map();
map = map.set('@', '')
map = map.set(64, '')
map = map.set(96, '')

The bug can be found here: src/Map.js#L324-325

Browser compatibility/targets?

What browsers are you targeting with this library?

One big usage of keywords I saw was delete, which won't work in IE8 (not my favorite either...). I have experimented with renaming every instance of delete to remove and it seems to work fine (though, accessing as a prop would probably work just as well).

Edit: I'm kind of erroneously assuming polyfilling missing things like in React is fine, but may not be fine with people

Edit2: I have implemented this fix in c52b58d. Let me know what you think.

Not updating value inside nested Map

Assume iam having the follwing Map :

var map = Immutable.fromJS({a:1, b:{d:2}, c:3})

Now i want to update the value of 'd' to 100....so that i tried like this :

1 .map = map.updateIn(['b','d'], 1000).
2. map = map.updateIn(['b','d'], value => 1000) // giving unexpected token >
3. map = map.updateIn(['b'], map => map.set['d',1000]) // giving unexpected token >

but not able to update the value..

could you please suggest me

Clashes with traceur.

I'm trying to use this package in an ES6 package which uses traceur for compilation.
It seems that the npm dist of this package includes it's own traceur runtime. It doesn't seem up to date with the latest traceur and is causing issues, can you at least check if traceurRuntime exists before defining your own? Happy to make a simple PR that does that if that's OK.

Want to delete a value form nested map

var nested = Immutable.fromJS({a:{b:{c:[3,4,5]}}});
Assume i am having above nested map...so i want to delete key(c) and its values also..
To update value we have updateIn() method,
like that , is there any method to delete ?
How to do that ? could you please help me

Bug in _setBounds causing buggy unshift.

Consider following example

function twice(l) {
  var a = Immutable.Range(0, l).toVector();
  return a.unshift.apply(a, a.toArray());
}

Now, try to call

> twice(12).toArray()
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

Nice, works as expected.

What about

> twice(32).toArray()
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]

Still good.

And now hold the breath:

> twice(33).toArray()
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, undefined ร— 32, 32]

Not so cool anymore!

Look at the lines in Vector.js at _setBounds:

while (newOrigin + offsetShift < 0) {
  // TODO: why only ever shifting over by 1?
  newRoot = new VNode(newRoot.array.length ? [,newRoot] : [], owner);
  offsetShift += 1 << newLevel;
  newLevel += SHIFT;
}

You already have the TODO explaining where the problem lies :-)

Not updating value inside Map

Hai all,

Iam having a map like : var map = Immutable.Map({a:1, b:{d:2}, c:3});

now i want to update value of 'd'...and i tried the following :

  1. map.set(['b','d']),1000)
    2.map = map.updateIn(['b', 'd'], function(value) { return 1000; });// i know it is for nested map...

and not able to update ...so could you please suggest me how to update that value

Using vector.shift() and vector.slice().toVector() does not result in the an equal object

Use case:
Create a subset of a Vector based on max vector length constraint.

Observation:
Using different techniques results in different objects. For example, using vector.shift() returns a Vector with the first item removed. Using vector.slice(1, vector.length).toVector() returns a Vector with the same values, but comparing the two resulting vectors for equality results in false.

Is the following the expected behavior even though the values for v2 and v3 came from the same v1 vector?

var v1 = Immutable.Vector(1, 2, 3, 4)
// Vector [ 1, 2, 3, 4 ]

var v2 = v1.shift()
// Vector [ 2, 3, 4 ]

var v3 = v1.slice(1, v1.length).toVector()
// Vector [ 2, 3, 4 ]

v2 === v3
// false

Map/filter looses the underlying collection type

Example:

var v = Immutable.Vector(1, 2, 3)
v.toString() // "Vector [ 1, 2, 3 ]"
var v2 = v.map(function (x) { return x + 1; })
v2.toString() // "Seq [ 2, 3, 4 ]"
v2.delete(0) // TypeError: undefined is not a function

So, one must issue toVector() after the transformation to get original structure type back. This is very inconvenient for a library user.

I understand this is due to laziness so possible options may be:

  • provide strict versions of map/filter/etc;
  • somehow preserve set of methods and observable behavior of the original data structure;
  • provide single method replacing toVector/toMap/etc and allowing to get original data structure after transformation back, e.g. force();
  • ...

TypeScript/npm import problem

The readme suggest this:

import Immutable = require('./node_modules/immutable/dist/Immutable');

However, npm doesn't guarantee that the module is at that location (as it doesn't make any guarantees to the relative location of the dependency).

The correct syntax would be

import Immutable = require('immutable');

This assumes that the compiler is provided with the ambient external declaration file.

Map Benchmarks

I added immutable-js to my Javascript persistent map benchmarks . My initial results suggest that there is still room for improvement, especially on aggregate operations, at least on these artificial, micro benchmarks.

Please take a look and let me know if there are any flaws with the immutable benchmarks.

I also got a few random errors in immutable during benchmarking:

js-hashtrie-benchmark/node_modules/immutable/dist/Map.js:195
  BitmapIndexedNode.prototype.set=function(ownerID, shift, hash, key, value, d
                                          ^
RangeError: Maximum call stack size exceeded

However I could not reliably repo to collect a more detailed stack. It could be caused by incorrect library usage on my part.

Provide count method

count method would be a nice addition. It's easier to read and comprehend than reduce used for similar purpose.

Example usage:

var coll = Immutable.Vector(1, 2, 3, 4);
// with count proposed
var numOds = coll.count(function (i) { return i % 2 === 1; });
// with reduce
var numOds2 = coll.reduce(function (acc, i) { return i % 2 === 1 ? acc + 1 : acc; }, 0);

.length of a filtered Sequence

After I create a IndexedSequence through filtering output, I want to get the length of the sequence such as so:

var provider = someObject;
var values = someObject;

var issues = values.get('issues').filter(function(issue) 
  return issue.get('provider').get('id') === provider.get('id');
});

console.log(issues.length); // This outputs undefined

console.log(issues.toJSON().length); // This outputs an actual number

It seems to me this has to do with lazy evaluation of sequences where toJSON trigger evaluation.

Is parsing it to JSON the correct way to output the length of this array or is there another way for evaluation to trigger.

potential Accepts Raw javascript ReadMe typo

Hey,
Immutable looks pretty cool!

While reading through the doc, I saw your merge example:

var map1 = Immutable.Map({a:1, b:2, c:3, d:4});
var map2 = Immutable.Map({c:10, a:20, t:30});
var obj = {d:100, o:200, g:300};
var map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 1000, t: 30, o: 2000, g: 300 }

I think the resultant Map should be:

// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }

Or is the obj meant to be in thousands?

Cheers,
Kyle

Map keys method result type differs from ES6

While I see the advantage of returning a Seq this does not conform to the pending ES6 Map/Set proposals. Is there any reason to not return an ES6 style Iterator and perhaps provide a different method keySeq?

Provide Map.update method

Currently Map.updateIn method is provided. But it's strange not to also have it's simpler counterpart update which wouldn't force to wrap single element into array.

I understand that one can get and set(v) but this is harder to read and less fluent API.

JSON

Hi, I've noticed you use .toJSON() to convert to JavaScript objects.

JSON is a data serialization format, converting it toJSON would have been to also call .stringify on it.

The objects produced by this call are also not always valid JSON or are even encodable/decodable as JSON.

I would recommend renaming .toJSON. I don't have any particularly good ideas on what to though.

Cloning API proposal

asMutable and asImmutablebehaviour aren't immediately obvious. How about cloneAsMutable and cloneAsImmutable?

clone also isn't obvious and is only there to retain API parity with the mutable version of clone.

The first point is more than just bikeshedding the wording if, say, cloneAsMutable works for mutable collections. This way, you can safely remove clone altogether.

Memory leak in UnorderedMap

Using UnorderedMap for longer time can consume lots of memory.

For example this code:

var map = Immutable.OrderedMap({'b': 'b'});
for(i = 0; i < operations; i++) {
    map = map.set('a', 'a');
    map = map.delete('a');
}

leads to this memory consumption :

operations Memory (kb)
1000 16000
10000 19000
100000 23000
1000000 58000
10000000 160000

It is because indexes are created sequentially and calling Vector.delete(index) does not free any memory.

The element b is there to ensure that UnorderedMap does not become empty and remove Vector.

prblem while retriving value from vector

var vect1 = Immutable.Vector(1, 2, 3, 4, 5);

now i want to retrive one by one value from the vector...
so i did :

vect1.map(function (value) {

});

its not even enter into the inside function....how to get values from vector ...could you please suggest me

Appropriate use of immutable.js with React

I have an immutable object which I am passing through as state this.setState(getStateFromStore()).
However it looses all its prototype functions related to immutable.js on trying to access the state.

It works fine when the state is set as: this.setState({data: getStateFromStore()}) which is kindof clunky and hackish.

This makes me wonder, what are the best practices on using Immutable.js data structures inside React views?

Map update() is missing

The immutable.d.ts documents an update method for Map. When I create a map with Immutable.Map({ name: 'foo', id: 42 }), there is no update method available. There is only updateIn. Is the code missing, or is the doc wrong?

Having trouble combining Vector::withMutations with Vector::splice

Should the following code example log true? On 2.0.4 it's logging false for me. Am I doing something wrong?

vector = Immutable.Vector(1, 2, 3)
a = vector.splice(1, 1, 'a', 'b')
b = vector.withMutations (v) -> v.splice(1, 1, 'a', 'b')
console.log Immutable.is(a, b)

Use RRB-Tree in Vector

I'm attempting to use vectors to represent the contents of 5-line tiles in Atom, and I'm keeping the groups of lines up to date with repeated applications of splice. After not very long, however, performance of iteration seems to degrade. Is this a known issue?

screenshot 2014-08-05 12 39 59

Why not use JavaScript getters?

I'm not sure if I'm overlooking something or if this is a stupid question, but... Why not use JavaScript getters?

JavaScript objects have support for getters: (works everywhere except on IE8)

Why not use them (optionally) instead of .get() and .getIn()? Especially for server-side code and for applications that aren't focusing IE8 this could be really helpful.

Possible bug in VNode removeBefore and removeAfter

I have noticed the following line of code in method VNode.removeBefore

if (index === 1 << level || this.array.length === 0) {
  return this;
}

For simplicity reasons, assume level is 0. In such a case we are dealing with pretty flat array of (max) length 32 containing only leafs. In such a case, calling removeBefore with index = 1 should remove the element with index 0.

However, in this case, it does nothing. Am I missing something?

Also, I have noticed that running

var a  = Immutable.Vector(1, 2, 3); 
var b = a.pop();
var c = b.pop();

Leads to strange fact that a._tail.array.length == 3 but both b and c tails are same and their length is 2. This happens only for lengths of tail == 2, it seems weird. Should it behave like this?

Newcap convention?

I know built-in Math is an exception to this, but it's a widely accepted convention to only capitalize constructor functions, and nothing else, in user JavaScript (see the JSHint newcap option).

Have you considered using immutable instead of Immutable in the documentation and examples?

Eg. This:

var Immutable = require('immutable');
var map = Immutable.Map({a:1, b:2, c:3});

Would become this:

var immutable = require('immutable');
var map = immutable.Map({a:1, b:2, c:3});

(etc)

Make toJS/fromJS somehow optionally preserve Set/OrderedMap types

It would be nice to add some serializable meta-information when toJSing immutable data structure containing sets and ordered maps so that fromJS could then create correct types back. For set it's rather trivial - just add some marker like type: '__set__' to the object. For ordered map it's harder and the result can take more space (to include indexes somehow). So this stuff probably should be optional (e.g. enable it by passing true as an additional parameter to toJS/fromJS).

PersistentArrayMap type

After considerable amounts of benchmarking we've found that for small maps (<= 8 keys) a specific PersistentArrayMap type outperforms a full blown PersistentHashMap. A PersistentArrayMap type also has the advantage of being quickly constructed from an array a la Immutable.Vector - particularly handy in a deserialization context.

nested objects

hi, immutable-data looks great! :)

i'd imagine using this with nested objects (a "store" Map that contains many "model" Maps that may contain "relational" Maps, Sets, etc). however, it appears fromObject and toObject don't support serializing and deserializing these nested objects.

var x = Im.Map.fromObject({ a: { b: { c: 1 } } });
// Map { "a": [object Object] }
x.getIn(['a','b','c'])
// undefined
x.toObject()
// { a: { b: { c: 1 }}}

and in reverse

var x = Im.Map.fromObject({ a: Im.Map.fromObject({ b: Im.Map.fromObject({ c: 1 }) }) })
// Map {"a": Map { "b": Map { "c": 1 } } }
x.getIn(['a','b','c'])
// 1
x.toObject()
// {a: Map { "b": Map { "c": 1 } } }

is this something that might be within scope of immutable-data?

Attaching a custom JS type / methods to a Map?

Hi, I'm using immutable-js for all data structures in a project of mine and am enjoying it very much. The only thing that bothers me is that I lose type safety / information when using Maps as a replacement for JS objects.

Say I use a Map to store information about a Person (like name and age). Is there some way to make it a real Person object and not a Map, so my code can say instanceof Person, and maybe even call custom methods on it?

Or is there another way to get some level of type safety when using Maps?

Most efficient way of building an indexed collection

Hello and thank your for the work here. It works like a charm with React and Flux :)

While the documentation is complete, it kinda lacks real life examples, and since I'm new to the immutable world, I have a hard time figuring out which structure I should pick in which case.

I'm not sure it's a good thing I open a new issue each time I struggle with something (that is going to happen) so maybe I could ask them all here and then bundle your answers in a PR so it can help other newbies.

I think my main use case (and many real life use cases) is: I get JSON from the server, it looks like:

[
  {
    "id": "aze12",
    "someField": "someValue",
    ....
  },
  {
    "id": "67azea",
    "someField": "someValue",
    ....
  }
]

I think the natural way of storing with Immutable is using OrderedMap. But what is the best way of building the map with "id" or "someField" as the index?
Currently I create an empty OrderedMap, then iterate over my parsed data, and set them one by one within a withMutations. It doesn't feel intuitive, what do you think?

Thank you in advance.

Incorrect behavior after groupBy

Example:

// having a set of ids
var ids = Immutable.Set('a', 'b', 'c');
// and some data
var data = Immutable.fromJS({a: {foo: 'bar'}, b: {foo: 'bar'}, c: {foo: 'bar'}})
// let's get data items by id
var items = ids.map(function (id) { return data.get(id); });
// and group them by 'foo'
var grouped = items.groupBy(function (o) { return o.get('foo'); })
// let's take the first item
var first = grouped.first();
// and to string it (notice the absence of comma between inner maps)
first.toString() // "Seq [ Map { foo: "bar" }Map { foo: "bar" }Map { foo: "bar" } ]"
// finally convert to array
first.toArray() // [undefined ร— 3]
// 3 times undefined??? WAT

But if we do items.toVector() just before groupBy, this scenario seems to work correctly.

I apologize for the long description, this thing was driving me crazy...

Functional API

I've been playing with the library and missed a functional API instead of a class based method one. To play with it i've made a little wrapper around the library that just adds the static methods to the classes making the last param the object on which the method will be invoked on.

npm - immutable-fns

That way we can do things like:

// On the immutable-js api
Immutable.Map({}).get('key')
// Added on this library
Immutable.Map.get('key', map)
var Immutable = require('immutable-fns');
var get = Immutable.Map.get;

// Create an immutable data structure
var m = Immutable.Map({a: 1});

// We can use the traditional api (obj.method(param)) or use the functional API
// (fn(...params, obj))
assert.equal(m.get('a'), get('a', m));

This kind of API is really useful sometimes, and doesn't conflict with the current API (that I have seen). This wrapper may serve to evaluate if this is worth pursuing on the library itself, and it does its job for the moment.

Thanks for your work on the immutable data structures, really hope to see them take off in js-land.

Provide Map.setIn()

What is the preferred way to set a nested value? I see that there is updateIn(), which kind of does what I want, but it has two problems:

  • It doesn't set new keys, it can only update already existing keys.
  • It uses an extraneous function for updating. The value I want to set is independent from the original value (undefined), so I don't need any function to determine the new value.

How would you add a new key 'd' with a single line? What's the API for this?

var mutableObject = {
  test: { test2: { a:1, b:2, c:3 } }
};
var immutableObject = Immutable.fromJS(mutableObject);

// desired mutation:
// {
//   test: { test2: { a:1, b:2, c:3, d:4 } }
// );

// with mutable objects it's very simple:
mutableObject.test.test2.d = 4;

// with immutable objects it doesn't seem to be possible:
immutableObject = immutableObject.setIn(['test', 'test2', 'd'], 4); // TypeError

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.